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

