SpringBoot中Oracle SYS_REF_CURSOR转Java List失败求助
解决Spring Boot 2.5.6调用Oracle存储过程返回SYS_REF_CURSOR得到null的问题
问题诊断
从提供的代码来看,导致返回null的核心问题集中在存储过程参数定义、JPA映射规则以及字段匹配这几个方面,具体如下:
- REF_CURSOR参数的类型指定错误
- Repository方法的返回值处理不符合JPA存储过程调用规范
- 实体字段与游标返回列的映射存在潜在不匹配风险
具体修正方案
1. 修正DTO中的存储过程参数定义
将REF_CURSOR参数的type从void.class改为OracleTypes.CURSOR,JPA需要明确游标类型才能正确解析结果;同时移除@GeneratedValue,因为findingID是从游标返回的已有值,无需JPA自动生成:
import oracle.jdbc.OracleTypes; import javax.persistence.*; @NamedStoredProcedureQueries({ @NamedStoredProcedureQuery( name = "getOpenFindings", procedureName = "OPEN_CODE_FINDINGS", resultClasses = OpenFindingDTO.class, parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "INPUT_DATE", type = String.class), @StoredProcedureParameter(mode = ParameterMode.REF_CURSOR, name = "C_OUTPUT", type = OracleTypes.CURSOR) } ) }) @Entity public class OpenFindingDTO { @Id @Column(name=Constants.FINDING_ID_PK) private String findingID; @Column(name=Constants.REVIEW_ID_PK) private String reviewID; @Column(name=Constants.REVIEW_ID_STR) private String reviewIDStr; // 原有getter/setter保持不变 }
2. 调整Repository方法定义
使用@Procedure的procedureName直接指定存储过程名,同时将泛型ID类型改为实体对应的String类型,确保参数名称与存储过程参数严格一致:
@Repository public interface OpenFindingRepository extends CrudRepository<OpenFindingDTO, String> { @Procedure(procedureName = "OPEN_CODE_FINDINGS") List<OpenFindingDTO> getOpenFindings(@Param("INPUT_DATE") String dataDate); }
3. 验证字段列名映射
确保Constants中定义的列名与存储过程游标返回的列名完全匹配(Oracle列名默认大写,注意大小写一致性)。如果游标返回列名与DTO字段名不同,需在@Column中明确指定正确列名,例如:
@Column(name = "FINDING_ID") // 替换为游标实际返回的列名 private String findingID;
4. 调试与日志排查
在Service中添加日志打印确认参数传递情况,同时开启JPA SQL日志查看存储过程调用细节:
public Object createFile() { Object obj=null; try { XSSFWorkbook workBook = toolUtil.createFile(); log.info("调用存储过程,输入日期:{}", Constants.INPUT_DATE); List<OpenFindingDTO> openFindingList = openFindingRepository.getOpenFindings(Constants.INPUT_DATE); log.info("存储过程返回结果数量:{}", openFindingList != null ? openFindingList.size() : "null"); } catch (SecurityException | IOException e) { log.error("调用存储过程失败", e); } return obj; }
在配置文件中开启JPA日志:
spring: jpa: show-sql: true properties: hibernate: format_sql: true use_sql_comments: true
替代方案:使用EntityManager手动调用存储过程
如果上述方案未解决问题,可通过EntityManager手动调用存储过程,灵活性更强便于调试:
@Service @Slf4j public class BBMReportServiceImpl implements BBMReportService { @PersistenceContext private EntityManager entityManager; public Object createFile() { Object obj=null; try { XSSFWorkbook workBook = toolUtil.createFile(); StoredProcedureQuery query = entityManager.createStoredProcedureQuery("OPEN_CODE_FINDINGS", OpenFindingDTO.class); // 注册并设置输入参数 query.registerStoredProcedureParameter("INPUT_DATE", String.class, ParameterMode.IN); query.setParameter("INPUT_DATE", Constants.INPUT_DATE); // 注册输出游标参数 query.registerStoredProcedureParameter("C_OUTPUT", OracleTypes.CURSOR, ParameterMode.REF_CURSOR); // 执行并获取结果 query.execute(); List<OpenFindingDTO> openFindingList = query.getResultList(); log.info("返回结果数量:{}", openFindingList.size()); } catch (SecurityException | IOException e) { log.error("调用失败", e); } return obj; } }
内容的提问来源于stack exchange,提问作者Sridhar
相关产品推荐
相关产品推荐

