You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法通过JDBC获取OUT参数中的二维关联数组问题排查

解决JDBC调用PL/SQL嵌套关联数组存储过程的问题

咱们一步步拆解你遇到的两个问题:

第一个错误:语法报错(ORA-06550 + PLS-00103)

你JDBC里定义的调用字符串多了个不必要的分号:

String proc = "{call TXN_CONSOLIDATION_PKG.TXN_CONSOLIDATION_PROC (?,?,?,?,?,?,?);}";

PL/SQL的CALL语句不需要末尾的分号,把它去掉就能解决这个语法问题:

String proc = "{call TXN_CONSOLIDATION_PKG.TXN_CONSOLIDATION_PROC (?,?,?,?,?,?,?)}";

第二个错误:参数类型不匹配(ORA-06550 + PLS-00306 + PLS-00418)

这是核心问题:JDBC的registerIndexTableOutParameter只能处理一维的PL/SQL索引表(关联数组),完全不支持嵌套的关联数组结构。你定义的final_txn_list_type是嵌套层级的(外层存储内层数组的索引表),JDBC无法识别这种嵌套类型,所以会报类型不匹配的错误。

这里给你两个可行的解决方案,优先推荐第一个,因为更简单易用:

方案1:改用REF CURSOR返回结果集(最推荐)

把输出参数换成SYS_REFCURSOR,这是JDBC处理PL/SQL返回多行数据最常用的方式,兼容性最好。

步骤1:修改PL/SQL包

先定义一个记录类型对应你的行结构,然后用SYS_REFCURSOR输出结果:

CREATE OR REPLACE PACKAGE TXN_CONSOLIDATION_PKG AS
    -- 定义记录类型,根据你实际的列数和类型调整字段
    TYPE txn_info_rec_type IS RECORD (
        col1 VARCHAR2(100),
        col2 VARCHAR2(100),
        col3 VARCHAR2(100)
        -- 继续添加你需要的列
    );
    
    PROCEDURE TXN_CONSOLIDATION_PROC (
        tableName IN VARCHAR2, 
        tableType IN VARCHAR2, 
        startDate IN VARCHAR2, 
        endDate IN VARCHAR2, 
        cardNumber IN VARCHAR2, 
        rowCount IN INTEGER,
        final_txn_list OUT SYS_REFCURSOR
    );
END TXN_CONSOLIDATION_PKG;
/

CREATE OR REPLACE PACKAGE BODY TXN_CONSOLIDATION_PKG AS
    PROCEDURE TXN_CONSOLIDATION_PROC (
        tableName IN VARCHAR2, 
        tableType IN VARCHAR2, 
        startDate IN VARCHAR2, 
        endDate IN VARCHAR2, 
        cardNumber IN VARCHAR2, 
        rowCount IN INTEGER,
        final_txn_list OUT SYS_REFCURSOR
    ) IS
    BEGIN
        -- 替换成你原来的重复行合并逻辑,最后用OPEN...FOR返回结果集
        -- 示例(根据你的实际逻辑调整):
        OPEN final_txn_list FOR
            SELECT col1, col2, col3
            FROM your_target_table
            WHERE card_number = cardNumber
              AND txn_date BETWEEN TO_DATE(startDate, 'MM/DD/YYYY HH24:MI:SS') 
                               AND TO_DATE(endDate, 'MM/DD/YYYY HH24:MI:SS')
            -- 这里加上你的合并逻辑,比如GROUP BY或者窗口函数
            FETCH FIRST rowCount ROWS ONLY;
    END TXN_CONSOLIDATION_PROC;
END TXN_CONSOLIDATION_PKG;
/

步骤2:修改JDBC代码处理REF CURSOR

Connection c = DriverManager.getConnection("url","username","pwd");
String proc = "{call TXN_CONSOLIDATION_PKG.TXN_CONSOLIDATION_PROC (?,?,?,?,?,?,?)}";
OracleCallableStatement s = (OracleCallableStatement)c.prepareCall(proc);

// 设置输入参数
s.setString(1, "F");
s.setString(2, "D");
s.setString(3, "01/01/2019 00:00:00");
s.setString(4, "11/01/2019 00:00:00");
s.setString(5, "Joe");
s.setInt(6, 100);

// 注册REF CURSOR类型的输出参数
s.registerOutParameter(7, OracleTypes.CURSOR);

s.execute();

// 获取并处理结果集
ResultSet rs = s.getCursor(7);
while (rs.next()) {
    // 根据列名或索引获取数据
    String col1 = rs.getString("col1");
    String col2 = rs.getString("col2");
    // 处理每一行的数据
    System.out.println("Col1: " + col1 + ", Col2: " + col2);
}

// 关闭资源(实际项目中建议用try-with-resources)
rs.close();
s.close();
c.close();

方案2:改用SQL层面的嵌套表类型(如果必须用数组)

如果你坚持要使用数组输出,需要把PL/SQL的关联数组转换成SQL层面的嵌套表类型(JDBC支持这种顶层SQL类型):

步骤1:创建SQL层面的类型

-- 创建内层的VARCHAR2数组类型(必须是SQL层面,不是PL/SQL)
CREATE OR REPLACE TYPE txn_info_array_sql_type IS TABLE OF VARCHAR2(100);
/

-- 创建外层的嵌套表类型
CREATE OR REPLACE TYPE final_txn_list_sql_type IS TABLE OF txn_info_array_sql_type;
/

步骤2:修改PL/SQL包使用这些SQL类型

CREATE OR REPLACE PACKAGE TXN_CONSOLIDATION_PKG AS
    PROCEDURE TXN_CONSOLIDATION_PROC (
        tableName IN VARCHAR2, 
        tableType IN VARCHAR2, 
        startDate IN VARCHAR2, 
        endDate IN VARCHAR2, 
        cardNumber IN VARCHAR2, 
        rowCount IN INTEGER,
        final_txn_list IN OUT final_txn_list_sql_type
    );
END TXN_CONSOLIDATION_PKG;
/

步骤3:修改JDBC代码处理SQL嵌套表

Connection c = DriverManager.getConnection("url","username","pwd");
String proc = "{call TXN_CONSOLIDATION_PKG.TXN_CONSOLIDATION_PROC (?,?,?,?,?,?,?)}";
OracleCallableStatement s = (OracleCallableStatement)c.prepareCall(proc);

// 设置输入参数
s.setString(1, "F");
s.setString(2, "D");
s.setString(3, "01/01/2019 00:00:00");
s.setString(4, "11/01/2019 00:00:00");
s.setString(5, "Joe");
s.setInt(6, 100);

// 注册输出参数,指定SQL类型的名称(注意是大写)
s.registerOutParameter(7, OracleTypes.ARRAY, "FINAL_TXN_LIST_SQL_TYPE");

s.execute();

// 获取嵌套数组并处理
Array outerArray = s.getArray(7);
Object[] outerElements = (Object[])outerArray.getArray();

for (Object innerObj : outerElements) {
    Array innerArray = (Array)innerObj;
    String[] innerElements = (String[])innerArray.getArray();
    // 处理每一行的列数据
    System.out.println("Row data: " + String.join(", ", innerElements));
}

// 关闭资源
s.close();
c.close();

这个方案需要注意用户需要有创建SQL类型的权限,而且处理逻辑比REF CURSOR复杂,所以优先推荐方案1。

内容的提问来源于stack exchange,提问作者user973179

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:06:57