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

如何通过Spring Data R2DBC调用带输入输出参数的Oracle存储过程

Spring Data R2DBC调用Oracle带输入输出参数的存储过程实现

依赖准备

确保项目中引入Oracle R2DBC驱动,Maven依赖如下:

<dependency>
    <groupId>oracle.r2dbc</groupId>
    <artifactId>oracle-r2dbc</artifactId>
    <version>23.3.0.23.10</version> <!-- 适配你的Oracle版本,推荐最新稳定版 -->
</dependency>

带基础输入输出参数的存储过程调用

假设你有如下Oracle存储过程:

CREATE OR REPLACE PROCEDURE GET_USER_INFO(
    IN_USER_ID IN NUMBER,
    OUT_USER_NAME OUT VARCHAR2,
    OUT_USER_AGE OUT NUMBER
) AS
BEGIN
    SELECT NAME, AGE INTO OUT_USER_NAME, OUT_USER_AGE FROM USERS WHERE ID = IN_USER_ID;
END;

使用DatabaseClient实现调用(Spring Data R2DBC的核心操作类):

import org.springframework.r2dbc.core.DatabaseClient;
import reactor.core.publisher.Mono;

@Service
public class UserService {

    private final DatabaseClient databaseClient;

    // 构造方法注入(推荐替代@Autowired)
    public UserService(DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }

    public Mono<UserInfo> getUserInfo(Long userId) {
        return databaseClient.sql("CALL GET_USER_INFO(:inUserId, :outUserName, :outUserAge)")
                // 绑定输入参数
                .bind("inUserId", userId)
                // 注册输出参数:必须指定类型,用bindNull占位
                .bindNull("outUserName", String.class)
                .bindNull("outUserAge", Integer.class)
                .fetch()
                .first() // 存储过程调用返回单条结果(包含输出参数)
                .map(row -> {
                    UserInfo info = new UserInfo();
                    info.setUserId(userId);
                    info.setUserName(row.get("outUserName", String.class));
                    info.setUserAge(row.get("outUserAge", Integer.class));
                    return info;
                });
    }

    // 定义对应的DTO
    public static class UserInfo {
        private Long userId;
        private String userName;
        private Integer userAge;

        // getter、setter省略
    }
}

带REF CURSOR输出参数的存储过程调用

如果存储过程返回游标结果集,示例存储过程:

CREATE OR REPLACE PROCEDURE LIST_DEPT_USERS(
    IN_DEPT_ID IN NUMBER,
    OUT_USER_CURSOR OUT SYS_REFCURSOR
) AS
BEGIN
    OPEN OUT_USER_CURSOR FOR SELECT ID, NAME, AGE FROM USERS WHERE DEPT_ID = IN_DEPT_ID;
END;

调用代码需要使用Oracle R2DBC提供的OracleRefCursor类型处理游标:

import oracle.r2dbc.OracleRefCursor;
import org.springframework.r2dbc.core.DatabaseClient;
import reactor.core.publisher.Flux;

@Service
public class DeptUserService {

    private final DatabaseClient databaseClient;

    public DeptUserService(DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }

    public Flux<User> listDeptUsers(Long deptId) {
        return databaseClient.sql("CALL LIST_DEPT_USERS(:inDeptId, :outUserCursor)")
                .bind("inDeptId", deptId)
                // 注册游标类型的输出参数
                .bindNull("outUserCursor", OracleRefCursor.class)
                .fetch()
                .all() // 遍历游标中的所有结果
                .map(row -> User.builder()
                        .id(row.get("ID", Long.class))
                        .name(row.get("NAME", String.class))
                        .age(row.get("AGE", Integer.class))
                        .build());
    }

    // User实体类
    public static class User {
        private Long id;
        private String name;
        private Integer age;

        // getter、setter、builder省略
    }
}

关键注意事项

  • 输出参数必须显式指定类型:R2DBC驱动需要提前知道输出参数的类型来分配缓冲区,因此必须用bindNull并传入对应类型,不能省略。
  • Oracle驱动兼容性:确保oracle-r2dbc驱动版本与你的Oracle数据库版本匹配,比如Oracle 19c对应驱动版本21.x,Oracle 21c/23c对应23.x版本。
  • Repository注解限制:Spring Data R2DBC的@Query注解目前不直接支持带输出参数的存储过程调用,因此推荐使用DatabaseClient实现这类场景。
  • 复杂类型处理:如果存储过程涉及自定义对象、数组等复杂类型,需要使用Oracle R2DBC提供的OracleObject、OracleArray等类型进行绑定和解析。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:57:08