Spring Boot调用PostgreSQL存储过程如何传递refcursor参数
在Spring Boot中调用你描述的带TEXT入参、refcursor INOUT参数的PostgreSQL存储过程,除了原生CallableStatement外,常用的有以下3种更简洁的实现方式:
方法1:使用Spring JDBC封装的SimpleJdbcCall
这是最通用的轻量实现,Spring自动帮你管理连接、资源释放,无需手动处理事务开启提交。
代码示例:
import org.springframework.jdbc.core.JdbcTemplate; 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 org.springframework.transaction.annotation.Transactional; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.List; import java.util.Map; @Repository public class ProcRepository { private final SimpleJdbcCall simpleJdbcCall; // Spring Boot会自动配置DataSource和JdbcTemplate,直接构造注入即可 public ProcRepository(JdbcTemplate jdbcTemplate) { this.simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate) .withSchemaName("myapp") .withProcedureName("MyProc") .declareParameters( new SqlParameter("in_user_id", Types.VARCHAR), new SqlOutParameter("user_roles", Types.REF_CURSOR) ); } // refcursor必须在事务内运行,必须加@Transactional注解 @Transactional public List<Integer> callMyProc(String userId) { List<Integer> result = new ArrayList<>(); Map<String, Object> outputMap = simpleJdbcCall.execute(userId); // 取出游标结果处理 try (ResultSet rs = (ResultSet) outputMap.get("user_roles")) { while (rs.next()) { result.add(rs.getInt(1)); } } catch (SQLException e) { throw new RuntimeException("读取存储过程返回结果失败", e); } return result; } }
方法2:使用Spring Data JPA的@Procedure注解
如果项目已经引入了Spring Data JPA组件,可以直接在Repository接口声明存储过程调用,代码最简洁。
代码示例:
import org.springframework.data.jpa.repository.query.Procedure; import org.springframework.data.repository.CrudRepository; import org.springframework.transaction.annotation.Transactional; import java.util.List; // 泛型第一个参数可替换为你项目中任意现有实体类,不影响存储过程调用 @Repository public interface ProcJpaRepository extends CrudRepository<YourAnyEntity, Long> { @Procedure(name = "myapp.MyProc") @Transactional List<Integer> callMyProc(String in_user_id); }
需要注意在application配置中指定PostgreSQL方言:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
方法3:使用MyBatis调用存储过程
如果项目用MyBatis作为持久层,直接在Mapper中声明即可,复杂结果集映射更灵活。
代码示例:
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.mapping.StatementType; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Mapper public interface ProcMapper { @Select("{call myapp.MyProc(#{in_user_id, mode=IN, jdbcType=VARCHAR}, #{user_roles, mode=INOUT, jdbcType=REF_CURSOR, javaType=java.sql.ResultSet})}") @Options(statementType = StatementType.CALLABLE) @Transactional List<Integer> callMyProc(String in_user_id); }
通用注意事项
- 所有涉及refcursor的存储过程调用必须在事务范围内执行,所以注解
@Transactional不能省略 - JDBC连接参数
escapeSyntaxCallMode=callIfNoReturn必须配置,建议直接加在JDBC URL中:jdbc:postgresql://azure.com/test?escapeSyntaxCallMode=callIfNoReturn&ssl=true - 如果返回的结果集字段较多,可直接映射为自定义Java实体类,三种方式都支持自动映射,无需手动遍历ResultSet
内容的提问来源于stack exchange,提问作者Abhishek Patil
相关产品推荐
相关产品推荐

