事务,Spring 引导程序 JDBC & R2DBC
Transactions, Spring Boot Starter JDBC & R2DBC
我正在尝试将使用 JDBC 模板的 Spring 版本 2.3.0.M3 的引导项目迁移到 R2DBC。该项目还使用了 Liquibase,所以我无法完全摆脱 JDBC。
我在项目中同时拥有 spring-boot-starter-data-r2dbc 和 spring-boot-starter-jdbc 依赖项,当我尝试 运行 我的一项测试:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,connectionFactoryTransactionManager
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1180)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:416)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:480)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:335)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
...
bean connectionFactoryTransaction 管理器在 Spring class R2dbcTransactionManagerAutoConfiguration:
中这样定义
@Bean
@ConditionalOnMissingBean(ReactiveTransactionManager.class)
public R2dbcTransactionManager connectionFactoryTransactionManager(ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory);
}
bean transactionManager 在 Spring class DataSourceTransactionManagerAutoConfiguration:
中这样定义
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
DataSourceTransactionManager transactionManager(DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
可以看出,@ConditionalOnMissingBean 注释包含不同的类型,这将导致创建两个 bean 的实例。
但是,在 Spring class TransactionAspectSupport 的 determineTransactionManager 方法中有这行代码:
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
由于两种事务管理器类型 DataSourceTransactionManager 和 R2dbcTransactionManager 都实现了 TransactionManager 接口,因此上述两种事务管理器 bean 都将匹配,并且会发生错误。
我现在想听听是否有人设法解决或解决了这个问题?
提前致谢!
在 M. Deinums 回答(谢谢!)的启发下,我将以下步骤应用于我的项目,之前失败的测试现在成功运行:
- 删除 spring-boot-starter-jdbc 依赖项。
- 添加对spring-jdbc的依赖。
- 添加对 HikariCP 的依赖 (com.zaxxer)。
- 添加 spring.liquibase 用户和密码属性(我已经有了 url 和更改日志属性)。
- 删除所有 spring.datasource 属性(我有 url 和 drive-class-name)。
我已经定义了 spring.r2dbc 属性用户名、密码和 url,我不需要更改。
更新:
另外,我在测试中使用了Testcontainers,无法分配静态端口。为了能够在 Liquibase 上配置数据库端口,我覆盖了一个 SpringLiquibase 类型的 bean 名称 liquibase 并在 liquibase bean 创建方法中创建了一个 DataSource(未作为 bean 公开)并将其设置在 liquibase bean 上。
spring-boot-starter-jdbc
和 spring-boot-starter-data-r2dbc
可以共存。有一个classorg.springframework.transaction.annotation.TransactionManagementConfigurer
可以用来解决冲突。
Spring Boot 2.3.0 似乎在存在 r2dbc 时禁用自动数据源配置。可以手动导入 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
class 使两者共存。
@Bean
TransactionManagementConfigurer transactionManagementConfigurer(ReactiveTransactionManager reactiveTransactionManager) {
return new TransactionManagementConfigurer() {
@Override
public TransactionManager annotationDrivenTransactionManager() {
return reactiveTransactionManager;
}
};
}
我正在尝试将使用 JDBC 模板的 Spring 版本 2.3.0.M3 的引导项目迁移到 R2DBC。该项目还使用了 Liquibase,所以我无法完全摆脱 JDBC。 我在项目中同时拥有 spring-boot-starter-data-r2dbc 和 spring-boot-starter-jdbc 依赖项,当我尝试 运行 我的一项测试:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,connectionFactoryTransactionManager
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1180)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:416)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:480)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:335)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
...
bean connectionFactoryTransaction 管理器在 Spring class R2dbcTransactionManagerAutoConfiguration:
中这样定义 @Bean
@ConditionalOnMissingBean(ReactiveTransactionManager.class)
public R2dbcTransactionManager connectionFactoryTransactionManager(ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory);
}
bean transactionManager 在 Spring class DataSourceTransactionManagerAutoConfiguration:
中这样定义 @Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
DataSourceTransactionManager transactionManager(DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
可以看出,@ConditionalOnMissingBean 注释包含不同的类型,这将导致创建两个 bean 的实例。 但是,在 Spring class TransactionAspectSupport 的 determineTransactionManager 方法中有这行代码:
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
由于两种事务管理器类型 DataSourceTransactionManager 和 R2dbcTransactionManager 都实现了 TransactionManager 接口,因此上述两种事务管理器 bean 都将匹配,并且会发生错误。
我现在想听听是否有人设法解决或解决了这个问题?
提前致谢!
在 M. Deinums 回答(谢谢!)的启发下,我将以下步骤应用于我的项目,之前失败的测试现在成功运行:
- 删除 spring-boot-starter-jdbc 依赖项。
- 添加对spring-jdbc的依赖。
- 添加对 HikariCP 的依赖 (com.zaxxer)。
- 添加 spring.liquibase 用户和密码属性(我已经有了 url 和更改日志属性)。
- 删除所有 spring.datasource 属性(我有 url 和 drive-class-name)。
我已经定义了 spring.r2dbc 属性用户名、密码和 url,我不需要更改。
更新:
另外,我在测试中使用了Testcontainers,无法分配静态端口。为了能够在 Liquibase 上配置数据库端口,我覆盖了一个 SpringLiquibase 类型的 bean 名称 liquibase 并在 liquibase bean 创建方法中创建了一个 DataSource(未作为 bean 公开)并将其设置在 liquibase bean 上。
spring-boot-starter-jdbc
和 spring-boot-starter-data-r2dbc
可以共存。有一个classorg.springframework.transaction.annotation.TransactionManagementConfigurer
可以用来解决冲突。
Spring Boot 2.3.0 似乎在存在 r2dbc 时禁用自动数据源配置。可以手动导入 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
class 使两者共存。
@Bean
TransactionManagementConfigurer transactionManagementConfigurer(ReactiveTransactionManager reactiveTransactionManager) {
return new TransactionManagementConfigurer() {
@Override
public TransactionManager annotationDrivenTransactionManager() {
return reactiveTransactionManager;
}
};
}