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

