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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 12:00:02