【问题标题】:Fetching an pl/sql array (as out parameter)获取 pl/sql 数组(作为输出参数)
【发布时间】:2016-07-21 09:39:02
【问题描述】:

我有一个 Oracle 存储过程,其中一个数组作为输入参数,一个数组作为输出参数。虽然输入参数已经正常工作,但我总是返回一个空值数组(尽管数组的长度是我所期望的)。

这只是一个测试环境,所以它是一个简单的例子:存储过程只接受输入数组并将值复制到输出数组和 varchar2 字段,所以我可以看到从输入数组复制到varchar2 字段可以正常工作,但不能用于输出数组。

我的 Java 代码如下:

    DriverManager.registerDriver(new OracleDriver());
    Connection conn = DriverManager.getConnection(
            "<ConnectionString>", "<user>", "<password>");
    conn.setAutoCommit(false);
    OracleConnection oracleConnection = (OracleConnection)conn;

    OracleCallableStatement stmt = (OracleCallableStatement)oracleConnection.prepareCall("call MYPACKAGE.TABLE_IN_TABLE_OUT( ?, ?, ? )");

    String[] inputStringArray = { "1", "2", "3", "4" };
    Array inputArray = oracleConnection.createOracleArray("MYPACKAGE.CHAR_TABLE", inputStringArray);

    stmt.setArray(1, inputArray);
    stmt.registerOutParameter(2, Types.ARRAY, "MYPACKAGE.ERG_TABLE");
    stmt.registerOutParameter(3, Types.VARCHAR);
    stmt.executeUpdate();

    Array resultArray = stmt.getArray(2);
    String [] resultStringArray = (String[])resultArray.getArray();
    String resultString = stmt.getString(3);

    System.out.println(resultString);
    for (String result : resultStringArray) {
        System.out.println(result);
    }

    conn.commit();
    conn.close();

存储过程

create or replace PACKAGE MYPACKAGE IS 
TYPE CHAR_TABLE IS TABLE OF CHAR(01) INDEX BY BINARY_INTEGER;
TYPE ERG_TABLE IS TABLE OF CHAR(01) INDEX BY BINARY_INTEGER;


PROCEDURE TABLE_IN_TABLE_OUT(
    inputArray  IN    CHAR_TABLE,
    outputArray  OUT   ERG_TABLE,
    resultString OUT VARCHAR2
);
END MYPACKAGE;

存储过程的实现:

create or replace PACKAGE BODY MYPACKAGE AS
  PROCEDURE TABLE_IN_TABLE_OUT(
     inputArray  IN    CHAR_TABLE,
     outputArray  OUT   ERG_TABLE,
    resultString OUT VARCHAR2) AS
  BEGIN
    FOR i IN 0..inputArray.last  loop
      outputArray(i) := inputArray(i);       
    end loop;
    resultString := '';
    FOR i IN 0..outputArray.last loop
      resultString := resultString || outputArray(i);
    end loop;
  END TABLE_IN_TABLE_OUT;
END MYPACKAGE;

这是输出:

VARCHAR2 result: 1234
Array result: null, null, null, null,  

在互联网和这个论坛上搜索了很多之后,我真的没有发现我做错了什么。

【问题讨论】:

    标签: arrays oracle stored-procedures jdbc


    【解决方案1】:

    不要使用在 PL/SQL 范围内(即在包中)定义的关联数组,而是使用在 SQL 范围内定义的集合。

    create or replace TYPE stringlist IS TABLE OF VARCHAR2(4000);
    /
    
    create or replace TYPE stringlist2 IS TABLE OF VARCHAR2(4000);
    /
    

    那么包是:

    CREATE OR REPLACE PACKAGE mypackage
    AS
      PROCEDURE table_in_table_out(
        inputArray   IN  stringlist,
        outputArray  OUT stringlist2,
        resultString OUT VARCHAR2
      );
    END mypackage;
    /
    
    CREATE OR REPLACE PACKAGE BODY mypackage
    AS
      PROCEDURE table_in_table_out(
        inputArray   IN  stringlist,
        outputArray  OUT stringlist2,
        resultString OUT VARCHAR2
      )
      IS
        i BINARY_INTEGER;
      BEGIN
        IF inputArray IS NULL THEN
          RETURN;
        END IF;
        outputArray := stringlist2();
        IF inputArray IS EMPTY THEN
          RETURN;
        END IF;
    
        -- Handle sparse arrays
        i := inputArray.FIRST;
        LOOP
          outputArray.EXTEND;
          outputArray(outputArray.LAST) := inputArray(i);
          resultString := resultString || inputArray(i);
          EXIT WHEN i = inputArray.LAST; 
          i := inputArray.NEXT(i);
        END LOOP;
      END;
    END mypackage;
    /
    

    在数据库中测试

    SET SERVEROUTPUT ON;
    
    DECLARE
      i stringList := StringList( 'A', 'C', 'F' );
      e stringlist2;
      s VARCHAR2(4000);
      n BINARY_INTEGER;
    BEGIN
      i.DELETE(2);
      n := i.FIRST;
      LOOP
        DBMS_OUTPUT.PUT_LINE( n || ': ' || i(n) );
        EXIT WHEN n = i.LAST;
        n := i.NEXT(n);
      END LOOP;
      mypackage.table_in_table_out( i, e, s );
      DBMS_OUTPUT.PUT_LINE( s );
      n := e.FIRST;
      LOOP
        DBMS_OUTPUT.PUT_LINE( n || ': ' || e(n) );
        EXIT WHEN n = e.LAST;
        n := e.NEXT(n);
      END LOOP;
    END;
    /
    

    Java

    try{
      Class.forName( "oracle.jdbc.OracleDriver" );
    
      Connection con = DriverManager.getConnection(
          "jdbc:oracle:thin:@localhost:1521:orcl",
          "username",
          "password"
      );
    
      OracleConnection oCon = (OracleConnection) con;
    
      OracleCallableStatement st = (OracleCallableStatement) con.prepareCall( "{ call mypackage.table_in_table_out( :chars, :ergs, :res )}" );
    
      ARRAY ia = oCon.createARRAY("STRINGLIST", new String[]{ "A", "C", "F"} );
      st.setARRAYAtName("chars", ia );
      st.registerOutParameter( 2, java.sql.Types.ARRAY, "VARCHAR2S_TABLE" );
      st.registerOutParameter( 3, java.sql.Types.VARCHAR );
    
      System.out.println( st.execute() );
      System.out.println( st.getString( 3 ) );
      String[] strs = (String[]) st.getARRAY(2).getArray();
    
      for ( String str : strs )
        System.out.println(str);
    
      st.close();
      con.close();
    } catch (ClassNotFoundException | SQLException ex) {
      System.out.println( ex.getMessage() );
      ex.printStackTrace();
    }
    

    【讨论】:

    • 谢谢。我试过这个并且它有效。但是您有使用“registerIndexTableOutParameter”和“getOraclePlsqlIndexTable”的经验吗?在我看来,这也是访问关联数组的可能性。我错了吗?
    【解决方案2】:

    最后(经过数小时的研究)我还找到了返回 index-by Tables 的方法,该方法现在有效。由于这是一种非常痛苦的方式,我想在这里分享我的解决方案:

        DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
        Connection conn = DriverManager.getConnection(
                "<connectionString>", "<user>", "<password>");
        conn.setAutoCommit(false);
        OracleConnection oracleConnection = (OracleConnection)conn;
        OracleCallableStatement stmt = (OracleCallableStatement)oracleConnection.prepareCall("BEGIN MYPACKAGE.TABLE_IN_TABLE_OUT( ?, ?, ? ); END;");
    
        String[] inputStringArray = { "1", "2", "3", "4", "5", "6"};
        Array inputArray = oracleConnection.createOracleArray("MYPACKAGE.CHAR_TABLE", inputStringArray);
    
        stmt.setArray(1, inputArray);
        stmt.registerIndexTableOutParameter(2, 100, OracleTypes.VARCHAR, 100);
        stmt.registerOutParameter(3, Types.VARCHAR);
        stmt.execute();
    
        String resultString = stmt.getString(3);
        String[] resultArray = (String[])stmt.getPlsqlIndexTable(2);
    
        System.out.println("VARCHAR2 result: " + resultString);
        System.out.print("Array result: ");
        for (String result : resultArray) {
            System.out.print(result + ", ");
        }
    

    我改变的最重要的事情:

    1. 我更改了调用字符串:

      oracleConnection.prepareCall("call MYPACKAGE.TABLE_IN_TABLE_OUT( ?, ?, ? )");
      

      oracleConnection.prepareCall("BEGIN MYPACKAGE.TABLE_IN_TABLE_OUT( ?, ?, ? ); END;");
      

      因为我在使用方法“registerIndexTableOutParameter”时遇到了“ORA-01484:数组只能绑定到 PL/SQL 语句”(请参阅​​下一点)。

    2. 不要以这种方式注册数组:

        stmt.registerOutParameter(2, Types.ARRAY, "MYPACKAGE.ERG_TABLE");
      

      我现在就是这样做的:

       stmt.registerIndexTableOutParameter(2, 100, OracleTypes.VARCHAR, 100);
      
    3. 要获取数组,我必须使用以下代码:

      String[] resultArray = (String[])stmt.getPlsqlIndexTable(2);
      

    仅此而已。希望这对其他人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      • 2021-02-18
      • 1970-01-01
      相关资源
      最近更新 更多