oracle 获取PL/SQL数组(作为输出参数)

k10s72fa  于 2023-06-29  发布在  Oracle
关注(0)|答案(3)|浏览(180)

我有一个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,

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

oymdgrw7

oymdgrw71#

不要使用在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();
}
khbbv19g

khbbv19g2#

最后(经过几个小时的研究),我发现了返回索引表的方法,它现在可以工作了。由于这是一个非常痛苦的方式,我想在这里分享我的解决方案:

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;");

因为我遇到了“ORA-01484:使用方法“registerIndexTableOutParameter”时,数组只能绑定到PL/SQL语句”(参见下一点)。
1.而不是以这种方式注册Array:

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

我现在是这样做的:

stmt.registerIndexTableOutParameter(2, 100, OracleTypes.VARCHAR, 100);

1.下面的代码是一个简单的例子:

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

这就是全部。希望这能帮助其他人。

xkftehaa

xkftehaa3#

我有类似的需求,但是,我必须从Java的存储过程中返回多个对象。
1.我在DB中创建了一个OBJECT类型。这是有5个属性
1.创建了一个TABLE类型的对象。(因为我会返回多个对象)
1.我已经设法在Object数组中获得了输出。
1.我的挑战是,我无法从对象数组中引用每个单独的对象。

stmt.execute();

     ARRAY simpleArray = (ARRAY) stmt.getArray(8);

     //Map map1 = con.getTypeMap();

     //map1.put("CLASS.T_LAC_TAB_O", ExpectedLacDataType.class);

     //con.setTypeMap(map1);

     System.out.println("Till here");

    Object[] values = (Object[])simpleArray.getArray();//working fine

在我得到了对象的值数组之后,我该如何前进?

相关问题