使用Spring Data R2DBC集成Oracle R2DBC时报发布者不支持多订阅者异常
异常产生原因
- 核心根因是Oracle R2DBC驱动与Spring R2DBC版本的兼容性问题:你当前使用的Spring R2DBC 5.3.9版本在连接获取逻辑中会对连接工厂返回的
Mono<Connection>发布者做多次订阅(比如连接校验、事务绑定场景),而低版本Oracle R2DBC驱动返回的连接发布者默认不支持多订阅,直接触发This publisher does not support multiple subscribers异常。 - 业务代码问题:如果你的代码中对同一个包含数据库连接获取的Flux/Mono实例做了多次订阅(比如重复调用
subscribe()、将R2dbcRepository返回的流赋值给变量多次复用、在外层对数据库流做重试/重复操作),也会触发该异常。 - 缺少连接池包装:原生Oracle R2DBC驱动返回的连接发布者本身不支持多订阅,没有连接池包装的情况下无法适配Spring框架的多订阅逻辑。
可行解决方案
- 方案1:升级依赖版本(推荐)
将Spring Boot版本升级到2.6.0及以上,对应Spring R2DBC版本升级到5.3.10+,该版本官方已经修复了ConnectionFactoryUtils的多订阅问题;同时将Oracle R2DBC驱动升级到1.0.0及以上正式版,该版本官方已经优化了连接发布者实现,支持多订阅场景。 - 方案2:自定义连接工厂添加缓存逻辑
如果暂时无法升级版本,可以手动包装原生连接工厂,给连接发布者添加缓存能力,示例代码如下:import io.r2dbc.spi.Connection; import io.r2dbc.spi.ConnectionFactory; import io.r2dbc.spi.ConnectionFactoryMetadata; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; @Bean public ConnectionFactory connectionFactory() { // 替换为你自己的Oracle R2DBC连接地址 ConnectionFactory originalFactory = ConnectionFactories.get("r2dbc:oracle://你的数据库地址:端口/服务名?user=用户名&password=密码"); return new ConnectionFactory() { @Override public Publisher<? extends Connection> create() { // 给连接发布者加缓存,支持多订阅 return Mono.from(originalFactory.create()).cache(); } @Override public ConnectionFactoryMetadata getMetadata() { return originalFactory.getMetadata(); } }; } - 方案3:引入R2DBC连接池
添加r2dbc-pool依赖,连接池返回的连接发布者天然支持多订阅,适配Spring框架逻辑,依赖配置如下:
引入依赖后在配置文件中添加<dependency> <groupId>io.r2dbc</groupId> <artifactId>r2dbc-pool</artifactId> </dependency>spring.r2dbc.pool.enabled=true即可启用连接池。 - 方案4:优化业务代码
不要提前将R2dbcRepository返回的Flux/Mono赋值给变量多次复用,每次数据库操作都重新生成流实例;重试、重复操作符要放在数据库操作的内层,避免对外层包含连接获取的流做多次订阅。
内容的提问来源于stack exchange,提问作者umesh karan
相关产品推荐
相关产品推荐

