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

Spring Boot中SimpleJdbcCall调用Oracle存储过程的REF CURSOR问题

解决Spring Boot 3中SimpleJdbcCall调用Oracle带自定义REF CURSOR存储过程的问题

问题根源分析

出现ORA-06550、PLS-00306错误的核心原因是未正确声明OUT类型的自定义REF CURSOR参数,以及可能存在的参数名称/类型匹配问题:

  • SimpleJdbcCall默认的自动参数检测无法识别Oracle自定义游标类型,导致参数类型不匹配
  • 若未显式声明所有参数,驱动可能无法正确生成符合存储过程要求的调用语句

正确实现方案

以下是可直接复用的代码实现,包含完整的参数声明、类型映射和结果处理:

1. 确保依赖正确(pom.xml)

使用适配Java 17和Oracle的JDBC驱动:

<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc11</artifactId>
    <scope>runtime</scope>
</dependency>

2. DAO层实现代码

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import oracle.jdbc.OracleTypes;

@Repository
public class DataDao {

    private final SimpleJdbcCall getDataProcedure;

    // 构造函数注入JdbcTemplate,初始化SimpleJdbcCall
    public DataDao(JdbcTemplate jdbcTemplate) {
        this.getDataProcedure = new SimpleJdbcCall(jdbcTemplate)
                // 指定schema、包名、存储过程名
                .withSchemaName("ABC")
                .withCatalogName("DATABASE_DATA")
                .withProcedureName("PROCEDURE_GET_DATA")
                // 启用按参数名称绑定(避免顺序错误)
                .withNamedBinding()
                // 显式声明所有参数,包括IN和OUT
                .declareParameters(
                        new SqlParameter("PARAM1", Types.VARCHAR),
                        new SqlParameter("PARAM2", Types.VARCHAR),
                        new SqlParameter("PARAM3", Types.VARCHAR),
                        // 声明OUT游标参数:类型为OracleTypes.CURSOR,指定RowMapper映射结果
                        new SqlOutParameter("P_RET_CUR", OracleTypes.CURSOR, new DataRowMapper())
                );
    }

    // 调用存储过程的方法
    public List<DataEntity> fetchData(String param1, String param2, String param3) {
        Map<String, Object> params = new HashMap<>();
        params.put("PARAM1", param1); // 传入null或空字符串均可,根据需求调整
        params.put("PARAM2", param2);
        params.put("PARAM3", param3);

        // 执行存储过程,获取结果
        Map<String, Object> resultMap = getDataProcedure.execute(params);
        // 从结果中取出游标数据(key为OUT参数的名称,需与存储过程定义一致)
        return (List<DataEntity>) resultMap.get("P_RET_CUR");
    }

    // 自定义RowMapper,映射游标结果到实体类
    private static class DataRowMapper implements RowMapper<DataEntity> {
        @Override
        public DataEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
            DataEntity entity = new DataEntity();
            // 根据游标返回的列名映射字段,替换为你的实际列名和实体属性
            entity.setColumn1(rs.getString("COLUMN_1"));
            entity.setColumn2(rs.getString("COLUMN_2"));
            entity.setColumn3(rs.getInt("COLUMN_3"));
            // 其他字段...
            return entity;
        }
    }
}

关键注意事项

  • 参数名称匹配:declareParameters中声明的参数名称必须与存储过程定义的参数名完全一致(包括OUT游标参数的名称,比如存储过程中若游标参数名为P_RET_CUR,则代码中必须用这个名称)
  • 游标类型处理:Oracle自定义的DATABASE_DATA.PRRETCUR本质是REF CURSOR,直接用OracleTypes.CURSOR即可正确映射,无需额外注册自定义类型
  • null vs 空字符串:若存储过程对NULL参数处理与空字符串不同,可将代码中的null替换为""(与SQL Developer中的调用保持一致)
  • 命名绑定:启用.withNamedBinding()确保参数按名称匹配,而非顺序,避免因参数顺序错误导致的异常

验证测试

调用fetchData方法时传入参数:

List<DataEntity> data = dataDao.fetchData(null, null, "123");

此调用逻辑与SQL Developer中的call ABC.DATABASE_DATA.PROCEDURE_GET_DATA('', '', '1234', :v_cur)完全对齐,可正常获取游标结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 04:23:18