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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 14:32:00