如何获取作为数组工作的存储过程输出参数?

Posted

技术标签:

【中文标题】如何获取作为数组工作的存储过程输出参数?【英文标题】:How do I get a stored procedure output parameter that is an array to work? 【发布时间】:2018-05-22 07:38:33 【问题描述】:

我一直在开发对存储过程的 Java/JDBC 调用并取得了一些成功。但是,当输出参数是字符串数组时,我被卡住了。我已经成功地将标量类型作为输入和输出参数,以及将字符串数组作为输入参数。

这是我拥有的 PL/SQL 代码:

TYPE StringArray IS TABLE OF VARCHAR2(32767) INDEX BY BINARY_INTEGER;

create or replace package body 
test is
  procedure upper(
    in_array     in StringArray,
    out_array    out StringArray
  ) is
    tmp StringArray := in_array;
  begin
    for i in 0..(tmp.count-1) loop
      tmp(i) := UPPER(tmp(i));
    end loop;
    out_array := tmp;
  end;
end test;

这是我的 Java 代码:

public void testString(Connection connection) 
    String[] values =  "alpha", "beta", "gamma" ;
    try 
       CallableStatement callableStatement = connection.prepareCall("begin test.upper(?, ?); end;");
       DelegatingConnection<OracleConnection> delegatingConnection = (DelegatingConnection<OracleConnection>) new DelegatingConnection(connection);
       OracleConnection oracleConnection = (OracleConnection) delegatingConnection.getInnermostDelegate();
       Array input oracleConnection.createOracleArray("STRINGARRAY", values);
       callableStatement.setObject("in_array", input);
       callableStatement.registerOutParameter("out_array", Types.ARRAY, "STRINGARRAY");
       callableStatement.execute();
       Array output = (Array)callableStatement.getObject("out_array");
       String[] result = (String[])output.getArray();
       System.out.println("Length: " + result.length);  // Prints "Length: 3"
       System.out.println("First: " + result[0]);       // Prints "First: null"
     (catch SQLException e) 
       // Handle error
    

如果我直接从 SQL 脚本调用 PL/SQL 存储过程,它就可以工作。所以我认为存储过程本身是可以的。

如果我通过 JDBC 调用存储过程,它会正常完成。使用调试语句,我确认values 已正确地从Java 客户端发送到in_array 中的存储过程。也就是说,接收到具有适当值的长度为 3 的数组。据我所知,out_array 被发送回 Java 客户端。但是,出了点问题。 result 的大小为 3,但所有元素都是 null

如果我检查 output,我可以看到它内部有一个长度为 38 的 byte[]。这些字节的子序列映射到“ALPHA”、“BETA”和“GAMMA”。所以看起来数据会返回到客户端,但它没有正确转换为String[]

我做错了什么?

【问题讨论】:

您面临的实际问题在哪里。在 PLSQL 块或 JAVA 调用中。你的问题不清楚 @XING 据我所知,PL/SQL 没问题,因为存储过程正常完成。但是,在 Java 客户端代码中,结果是一个长度正确但包含 null 元素的数组。所以数据在客户端没有被正确转换。基于此,我认为问题出在 Java 端,但你永远不知道。 也许使用 vararrays 或嵌套表而不是关联数组(index-by-tables)。他们在the documentation 中写道,他们的 JDBC 驱动程序支持 varrarrays 和嵌套表,文档中没有提到 index-by-tables。 是什么让数组具有关联性?是二进制索引吗? 是的。 table of x index by y 是一种 PL/SQL 关联数组类型。我不懂 Java,但兼容 SQL 的集合类型可能会给你更多选择。 【参考方案1】:

不要使用关联数组 - 使用集合:

CREATE TYPE StringArray IS TABLE OF VARCHAR2(4000);
CREATE TYPE CLOBArray   IS TABLE OF CLOB;

那么你可以这样做:

public void testString(Connection connection) 
  String[] values =  "alpha", "beta", "gamma" ;
  try 
    OracleConnection oc = (OracleConnection) connection;

    ARRAY stringArray = oc.createARRAY( "STRINGARRAY", values ); // Upper case identifier

    OracleCallableStatement st = (OracleCallableStatement) oc.prepareCall(
      "begin test.upper( :in_array, :out_array ); end;"
    );

    st.setARRAYAtName( "in_array", stringArray );
    st.registerOutParameter( "out_array", Types.ARRAY, "STRINGARRAY"); // Upper case again
    st.execute();

    String[] result = (String[])st.getARRAY( 2 ).getArray();

    System.out.println("Length: " + result.length);
    System.out.println("First: " + result[0]);
   (catch SQLException e) 
    // Handle error
  

如果您必须使用 PL/SQL 关联数组作为过程的输入,那么编写一个函数,该函数接受一个集合并输出一个适当类型的关联数组,然后调用它:

BEGIN TEST.UPPER( TO_ASSOC_ARRAY( :in_collection ), :out_array ); END;

注意:此答案假设在 ojdbc6.jar 中使用 Oracle 驱动程序,它似乎没有 OracleConnection.createOracleArray() method,但希望它可以移植,将 oracle.sql.ARRAY 更改为 java.sql.Array 并使用更新的方法。

【讨论】:

我很确定我们使用的是ojdbc7.jar。这有关系吗?你会改变你的答案吗? 关键提示 - 不要使用关联数组 - 帮助我解决了我的问题。通过简单地更改out_array 的类型,我能够从存储过程中接收到一个字符串数组。我不需要更改任何 Java 客户端代码。

以上是关于如何获取作为数组工作的存储过程输出参数?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle PL/SQL:如何使用可变数组作为输出参数执行过程?

如何使用多个输出参数执行存储过程?

输入对象数组作为输入并返回一个数组作为存储过程的输出

临时表作为存储过程中的输出参数

如何在数组中获取存储过程的输出值

如何从存储过程中获取输出参数结果和查询结果?