JDBC:Oracle 存储过程返回嵌套表

Posted

技术标签:

【中文标题】JDBC:Oracle 存储过程返回嵌套表【英文标题】:JDBC: Oracle stored procedure returns nested table 【发布时间】:2016-04-17 09:19:57 【问题描述】:

我在 oracle(12c) 中有一个存储过程:

PROCEDURE work(a OUT VARCHAR2, b OUT SYS_REFCURSOR)

当我调用程序时,游标的结果包含行,该行由一些原始值+自定义类型组成:

CREATE TYPE ELEM AS OBJECT(ElemID INTEGER))
CREATE TYPE LIST AS TABLE OF ELEM

所以,结果是这样的:

vcursor:
------------------
COL1 COL2 LIST
------------------
A    B    LIST([ELEM],[ELEM])
C    D    LIST([ELEM],[ELEM],[ELEM])
E    F    LIST([ELEM])

有一个关于集合Oracle tutorial 的教程(如果链接丢失,请谷歌搜索“使用 Oracle 集合”)。到目前为止,本教程对我没有任何帮助。

这是我在 java 中所做的:

Connection conn = ...
CallableStatement statement = conn.prepareCall("call work()");
statement.registerOutParameter(1, OracleTypes.VARCHAR);
statement.registerOutParameter(2, OracleTypes.CURSOR);
statement.execute();

ResultSet rs = statement.getResultSet(2);
// here should be the code which retrieves the nested table elements
rs.next()
Array list = rs.getArray(3); //oracle.sql.ARRAY
Object[] elems = (Object[]) list.getArray(); // doesn't work
// I also tried list.getResultSet()

在调试下,我确实看到如果我转换为 oracle.sql.ARRAY 并调用 getOracleArray(),那么我会看到正确的大小和带有 Struct[] 的 Datum[] 数组,但是普通的 sql 方法什么也没给我。例如 getResultSet from array 返回一个 ResultSet , next 总是返回 true,但没有元数据和实际数据。 getArray() 返回 numElements = -1 等的数组。

我尝试玩 typeMap 但没有运气:

Map map = conn.getTypeMap();
map.put("ELEM", Elem.class); 
//map.put("SCHEMA.ELEM", Elem.class); 
//map.put("LIST", Elem.class); 
//map.put("SCHEMA.LIST", Elem.class); 
conn.setTypeMap(map);

请帮助和建议。提前致谢。

【问题讨论】:

【参考方案1】:

恐怕我很难重现您所看到的行为。我试图整理一些可以重现您的问题的代码,但它奏效了。代码如下,希望对你有用。

我为 ELEM 类型使用了类型映射条目,但没有为 LIST 使用类型映射条目。我在 CallableStatement 接口上也找不到getResultSet(int) 方法,所以我改用getObject(int) 并将其转换为ResultSet

以防万一,我在 Windows 10 x64 上使用 Oracle 11g XE 11.2.0.2.0、Java 1.8.0_60、ojdbc7.jar 版本 12.1.0.2.0。我也尝试了 ojdbc6.jar 和 ojdbc5.jar 的版本,结果是一样的。

SQL:

CREATE TYPE elem AS OBJECT(ElemID INTEGER);
/

CREATE TYPE list AS TABLE OF elem;
/

CREATE OR REPLACE PROCEDURE work(a OUT VARCHAR2, b OUT SYS_REFCURSOR)
AS
BEGIN
  a := 'test 1234';
  OPEN b FOR
    SELECT 'A' AS col1, 'B' AS col2, list(elem(14), elem(17)) FROM DUAL
     UNION ALL
    SELECT 'C' AS col1, 'D' AS col2, list(elem(8), elem(4), elem(11)) FROM DUAL
     UNION ALL
    SELECT 'E' AS col1, 'F' AS col2, list(elem(-1)) FROM DUAL;
END work;
/

Elem.java:

import java.sql.*;

public class Elem implements SQLData 

    private Integer elementId;

    public String getSQLTypeName()  return "ELEM"; 

    public void readSQL(SQLInput input, String typeName) throws SQLException 
        elementId = input.readInt();
    

    public void writeSQL(SQLOutput output) 
        throw new UnsupportedOperationException();
    

    public String toString() 
        return "<Element " + elementId + ">";
    

JDBCStructs.java:

import java.sql.*;
import java.util.*;

public class JDBCStructs 
    public static void main(String[] args) throws Exception 
        Connection c = ...

        Map<String, Class<?>> map = c.getTypeMap();
        map.put("ELEM", Elem.class);
        c.setTypeMap(map);        

        CallableStatement cs = c.prepareCall("call work(?, ?)");
        cs.registerOutParameter(1, Types.VARCHAR);
        cs.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);
        cs.execute();
        String a = cs.getString(1);
        System.out.println("Got string " + a);
        ResultSet rSet = (ResultSet)cs.getObject(2);
        while (rSet.next()) 
            String col1 = rSet.getString(1);
            String col2 = rSet.getString(2);
            Array list = (Array)rSet.getObject(3);
            System.out.println("Got " + col1 + ", " + col2 + " and " + Arrays.toString((Object[])list.getArray()));
        
    

输出:

Got string test 1234
Got A, B and [<Element 14>, <Element 17>]
Got C, D and [<Element 8>, <Element 4>, <Element 11>]
Got E, F and [<Element -1>]

【讨论】:

我用的是ojdbc6-6.0。我很惊讶它对你有用。不幸的是,我们决定将嵌套表更改为包含对象的 xml 字符串,以便我们可以将其解析回单独的元素。我接受答案,如果我再次遇到同样的问题,我会尝试验证代码。非常感谢!

以上是关于JDBC:Oracle 存储过程返回嵌套表的主要内容,如果未能解决你的问题,请参考以下文章

要使用嵌套表插入表的Oracle存储过程?

带有 PL SQL 表类型参数的 Oracle 存储过程的 JDBC 调用

JDBC调用存储过程的例子

Java Spring JDBC Oracle存储过程返回null [重复]

JDBC取消Oracle存储过程调用

oracle存储过程 中把临时表数据 返回结果集