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
相关产品推荐
相关产品推荐

