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

如何用Spring SimpleJdbcCall调用PostgreSQL返回refcursor的存储过程并取数

搞定Spring SimpleJdbcCall调用PostgreSQL返回Refcursor的存储过程

嘿,我懂你遇到的麻烦了——直接用默认的SimpleJdbcCall调用返回refcursor的PostgreSQL存储过程,Spring根本不知道该怎么处理这个游标类型对吧?别慌,我给你整理了一套能正常运行的代码,还有背后的原因说明。

首先得搞清楚为啥之前的代码不行:你没告诉Spring这个存储过程返回的是refcursor类型,它默认不会自动识别和提取游标里的数据。所以咱们要做的就是显式配置返回参数类型,再把结果集映射到你需要的对象或者直接读取。

先准备实体类(可选但推荐)

如果想用实体类来接收数据,先整个Emp类:

public class Emp {
    private Integer empId;
    private String firstName;

    // 别忘了添加getter和setter方法
    public Integer getEmpId() { return empId; }
    public void setEmpId(Integer empId) { this.empId = empId; }
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
}

正确的SimpleJdbcCall调用代码

// 初始化SimpleJdbcCall,关键是声明返回的refcursor参数和结果映射
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
        .withProcedureName("show_emps")
        // 声明输出参数为REF_CURSOR,参数名随便取,后面取结果对应上就行
        .declareParameters(new SqlOutParameter("emp_cursor", Types.REF_CURSOR))
        // 把游标里的结果集映射到Emp对象,这里用lambda简化RowMapper
        .returningResultSet("emp_cursor", (rs, rowNum) -> {
            Emp emp = new Emp();
            emp.setEmpId(rs.getInt("emp_id"));
            emp.setFirstName(rs.getString("first_name"));
            return emp;
        });

// 执行存储过程,因为没有输入参数,传空Map即可
Map<String, Object> resultMap = jdbcCall.execute(Collections.emptyMap());

// 从结果Map里取出映射好的员工列表
List<Emp> employees = (List<Emp>) resultMap.get("emp_cursor");

// 现在就可以遍历数据啦
for (Emp emp : employees) {
    System.out.printf("员工ID:%d,姓名:%s%n", emp.getEmpId(), emp.getFirstName());
}

不想用实体类?直接读ResultSet也行

如果不需要实体类,也可以用RowCallbackHandler逐行处理数据:

SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
        .withProcedureName("show_emps")
        .declareParameters(new SqlOutParameter("emp_cursor", Types.REF_CURSOR));

jdbcCall.execute(new MapSqlParameterSource(), new RowCallbackHandler() {
    @Override
    public void processRow(ResultSet rs) throws SQLException {
        System.out.printf("员工ID:%d,姓名:%s%n", rs.getInt("emp_id"), rs.getString("first_name"));
    }
});

备选方案:直接用CallableStatement

要是觉得SimpleJdbcCall配置有点麻烦,也可以直接用JDBC原生的CallableStatement调用,逻辑更直观:

jdbcTemplate.execute((Connection connection) -> {
    // 调用存储过程的语法:{ ? = call 存储过程名() }
    CallableStatement cs = connection.prepareCall("{ ? = call show_emps() }");
    // 注册输出参数为REF_CURSOR
    cs.registerOutParameter(1, Types.REF_CURSOR);
    // 执行调用
    cs.execute();
    // 获取游标对应的ResultSet
    ResultSet rs = (ResultSet) cs.getObject(1);
    // 遍历结果
    while (rs.next()) {
        System.out.printf("员工ID:%d,姓名:%s%n", rs.getInt("emp_id"), rs.getString("first_name"));
    }
    // 别忘了关闭资源
    rs.close();
    cs.close();
    return null;
});

这些方法都能解决你的问题,选哪种全看你自己的需求~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 09:43:13