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

SpringJDBC调用含TABLE OF NUMBER参数的存储过程报错求助

问题描述

在Spring JDBC中调用Oracle存储过程MY_SP,该过程包含TABLE OF NUMBER类型的输入参数PARAM1,存储过程定义如下:

PROCEDURE MY_SP(
    PARAM1 IN TABLE OF NUMBER,
    PARAM2 IN NUMBER
);

传入单元素整数数组(如[1])是合法的,但直接声明参数为OracleTypes.ARRAY并传入Integer[]时,出现报错:

Caused by: java.sql.SQLException: Fail to convert to internal representation: [Ljava.lang.Integer;@16a25973
    at oracle.sql.ARRAY.toARRAY(ARRAY.java:308)

旧方案使用已废弃的ArrayDescriptor构建SQL Array,但属于旧版Spring JDBC用法。当前采用现代Spring JDBC的StoredProcedure接口实现(包含compile()),无法直接获取数据库连接,寻求可行解决思路。现有代码实现如下:

@Component
public class MySP extends StoredProcedure {
    
    private RowMapper<List<Object>> rowMapper = new MyMapper();
    
    public MySP(@Autowired DataSource dataSource) {
        super(dataSource, "MYPACKAGE.MY_SP");

        declareParameter(new SqlParameter("PARAM1", OracleTypes.ARRAY));
        //...其他参数声明
        compile();
   }

   public String callSP(Integer[] intArray) {
        //intArray为单元素整数数组,例如[1]
        Map<String, Object> inputParameters = new HashMap<>();
        inputParameters.put("PARAM1", intArray);

        Map<String, Object> output = super.execute(inputParameters);
        // 处理结果...
   }
}
解决思路

1. 使用SqlValue接口包装数组(推荐)

Spring JDBC的SqlValue接口允许自定义参数处理逻辑,在参数设置时自动获取连接并创建Oracle兼容的ARRAY对象,无需手动管理连接:

修改callSP方法,将Integer[]包装为自定义SqlValue:

public String callSP(Integer[] intArray) {
    Map<String, Object> inputParameters = new HashMap<>();
    inputParameters.put("PARAM1", new SqlValue() {
        @Override
        public void setValue(PreparedStatement ps, int paramIndex) throws SQLException {
            Connection conn = ps.getConnection();
            // 替换为Oracle中实际定义的TABLE OF NUMBER类型别名(例:MYPACKAGE.NUMBER_TABLE)
            Array oracleArray = conn.createArrayOf("MYPACKAGE.NUMBER_TABLE", intArray);
            ps.setArray(paramIndex, oracleArray);
        }

        @Override
        public void cleanup() {
            // 可选:按需清理资源
        }
    });

    Map<String, Object> output = super.execute(inputParameters);
    // 处理结果...
}

参数声明保持不变,核心是确保createArrayOf的第一个参数与数据库中定义的数组类型名称完全一致。

2. 自定义SqlParameter实现

如果需要复用数组参数的处理逻辑,可以自定义SqlParameter并重写setValue方法:

public class OracleArraySqlParameter extends SqlParameter {
    private final String arrayTypeName;

    public OracleArraySqlParameter(String name, String arrayTypeName) {
        super(name, OracleTypes.ARRAY);
        this.arrayTypeName = arrayTypeName;
    }

    @Override
    public void setValue(PreparedStatement ps, int paramIndex, Object value) throws SQLException {
        if (value instanceof Integer[]) {
            Connection conn = ps.getConnection();
            Array oracleArray = conn.createArrayOf(arrayTypeName, (Integer[]) value);
            ps.setArray(paramIndex, oracleArray);
        } else {
            super.setValue(ps, paramIndex, value);
        }
    }
}

然后在构造函数中替换原参数声明:

public MySP(@Autowired DataSource dataSource) {
    super(dataSource, "MYPACKAGE.MY_SP");
    // 替换为实际的数组类型名称
    declareParameter(new OracleArraySqlParameter("PARAM1", "MYPACKAGE.NUMBER_TABLE"));
    //...其他参数声明
    compile();
}

调用时直接传入Integer[]即可,无需额外包装。

3. 切换为SimpleJdbcCall(现代Spring JDBC推荐方式)

如果允许调整实现方式,SimpleJdbcCall是Spring官方推荐的存储过程调用工具,支持更简洁的参数配置:

@Component
public class MySPCaller {
    private final SimpleJdbcCall simpleJdbcCall;

    public MySPCaller(DataSource dataSource) {
        this.simpleJdbcCall = new SimpleJdbcCall(dataSource)
               .withProcedureName("MY_SP")
               .withSchemaName("MYPACKAGE")
               .declareParameters(
                    new SqlParameter("PARAM1", OracleTypes.ARRAY, "MYPACKAGE.NUMBER_TABLE"),
                    new SqlParameter("PARAM2", OracleTypes.NUMBER)
                );
    }

    public String callSP(Integer[] intArray, Integer param2) {
        SqlParameterSource params = new MapSqlParameterSource()
               .addValue("PARAM1", intArray, OracleTypes.ARRAY, "MYPACKAGE.NUMBER_TABLE")
               .addValue("PARAM2", param2);

        Map<String, Object> output = simpleJdbcCall.execute(params);
        // 处理结果...
    }
}

通过SqlParameter的第三个参数指定Oracle数组类型名称,Spring会自动完成数组到Oracle ARRAY的转换,无需手动操作连接。

内容的提问来源于stack exchange,提问作者gene b.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 07:35:32