CallableStatement + registerOutParameter + multiple row result

前端 未结 2 1844
傲寒
傲寒 2020-12-21 11:53

I\'ve got a SQL statement of the form:

BEGIN\\n 
UPDATE tab 
SET stuff
WHERE stuff
RETURNING intA, intB, stringC
INTO ?,?,?

I\'ve registere

相关标签:
2条回答
  • 2020-12-21 12:09

    I believe you can achieve what you are looking for, but you will need to handle PL/SQL arrays rather than cursors or result sets. Below is a demonstration.

    I have a table, called TEST, with the following structure:

    SQL> desc test;
     Name                                      Null?    Type
     ----------------------------------------- -------- -----------------
    
     A                                                  NUMBER(38)
     B                                                  NUMBER(38)
     C                                                  NUMBER(38)
    
    

    and containing the following data:

    SQL> select * from test;
    
             A          B          C
    ---------- ---------- ----------
             1          2          3
             4          5          6
             7          8          9
    

    I need to create an array type for each type of column used. Here, I only have NUMBERs, but if you have one or more VARCHAR2 columns as well, you'll need to create a type for those too.

    SQL> create type t_integer_array as table of integer;
      2  /
    
    Type created.
    

    The table and any necessary types are all we need to set up in the database. Once we've done that, we can write a short Java class that does an UPDATE ... RETURNING ..., returning multiple values to Java:

    import java.math.BigDecimal;
    import java.util.Arrays;
    import java.sql.*;
    import oracle.sql.*;
    import oracle.jdbc.*;
    
    public class UpdateWithBulkReturning {
        public static void main(String[] args) throws Exception {
            Connection c = DriverManager.getConnection(
                "jdbc:oracle:thin:@localhost:1521:XE", "user", "password");
    
            c.setAutoCommit(false);
    
            /* You need BULK COLLECT in order to return multiple rows. */
            String sql = "BEGIN UPDATE test SET a = a + 10 WHERE b <> 5 " +
                         "RETURNING a, b, c BULK COLLECT INTO ?, ?, ?; END;";
    
            CallableStatement stmt = c.prepareCall(sql);
    
            /* Register the out parameters.  Note that the third parameter gives
             * the name of the corresponding array type. */
            for (int i = 1; i <= 3; ++i) {
                stmt.registerOutParameter(i, Types.ARRAY, "T_INTEGER_ARRAY");
            }
    
            /* Use stmt.execute(), not stmt.executeQuery(). */
            stmt.execute();
    
            for (int i = 1; i <= 3; ++i) {
                /* stmt.getArray(i) returns a java.sql.Array for the output parameter in
                 * position i.  The getArray() method returns the data within this
                 * java.sql.Array object as a Java array.  In this case, Oracle converts
                 * T_INTEGER_ARRAY into a Java BigDecimal array. */
                BigDecimal[] nums = (BigDecimal[]) (stmt.getArray(i).getArray());
                System.out.println(Arrays.toString(nums));
            }
    
            stmt.close();
            c.rollback();
            c.close();
        }
    }
    

    When I run this, I get the following output:

    C:\Users\Luke\stuff>java UpdateWithBulkReturning
    [11, 17]
    [2, 8]
    [3, 9]
    

    The outputs displayed are the values returned from the columns A, B and C respectively. There are only two values for each column since we filtered out the row with B equal to 5.

    You might want the values grouped by row instead of grouped by column. In other words, you might want the output to contain [11, 2, 3] and [17, 8, 9] instead. If that's what you want, I'm afraid you'll need to do that part yourself.

    0 讨论(0)
  • 2020-12-21 12:19

    To build upon what Luke Woodward answered and to refine my previous answer, you can create an Oracle type, use it to temporarily store data, and then return a sys_refcursor with your updates.

    Create the new type:

    CREATE OR REPLACE TYPE rowid_tab AS TABLE OF varchar2(30);
    

    Create the database function:

    CREATE OR REPLACE
    FUNCTION update_tab
    RETURN sys_refcursor
    IS
        ref_cur sys_refcursor;
        v_tab rowid_tab;
    BEGIN
        UPDATE tab 
        SET intA = intA+2
          , intB = intB*2
          , stringC = stringC||' more stuff.'
        RETURNING ROWID
        BULK COLLECT INTO v_tab;
    
        OPEN ref_cur FOR 
            WITH DATA AS (SELECT * FROM TABLE(v_tab))
            SELECT intA, intB, stringC
            FROM tab
            where rowid in (select * from data);
        RETURN ref_cur;
    END;
    

    Now, call the function in your java:

    import java.math.BigDecimal;
    import java.util.Arrays;
    import java.sql.*;
    import oracle.sql.*;
    import oracle.jdbc.*;
    
    public class StructTest {    
        public static void main(String[] args) 
            throws Exception 
        {   
            System.out.println("Start...");
    
            ResultSet results = null;
            Connection c = DriverManager.getConnection( "jdbc:oracle:thin:@localhost:1521:xe", "scott", "tiger");
            c.setAutoCommit(false);        
    
            String sql = "begin ? := update_tab(); end;";
            System.out.println("sql = "+sql);
            CallableStatement stmt = c.prepareCall(sql);        
            /* Register the out parameter. */
            System.out.println("register out param");
            stmt.registerOutParameter(1, OracleTypes.CURSOR);
            // get the result set
            stmt.execute();
            results = (ResultSet) stmt.getObject(1);
            while (results.next()){
                System.out.println("intA: "+results.getString(1)+", intB: "+results.getString(2)+", stringC: "+results.getString(3));
            }
            c.rollback();        
            c.close();    
        }
    }
    

    With my test data, I got the following results:

    intA: 3, intB: 4, stringC: a more stuff.
    intA: 6, intB: 10, stringC: C more stuff.
    intA: 3, intB: 4, stringC: a more stuff.
    
    0 讨论(0)
提交回复
热议问题