简体   繁体   English

事务,Spring Boot Starter JDBC & R2DBC

[英]Transactions, Spring Boot Starter JDBC & R2DBC

I am trying to migrate a Spring Boot project, version 2.3.0.M3, that have used JDBC template to R2DBC.我正在尝试将使用 JDBC 模板的 Spring Boot 项目版本 2.3.0.M3 迁移到 R2DBC。 The project also uses Liquibase so I cannot get rid of JDBC altogether.该项目还使用 Liquibase,因此我无法完全摆脱 JDBC。 I have both the spring-boot-starter-data-r2dbc and the spring-boot-starter-jdbc dependencies in the project with which I get the following exception when trying to run one of my tests:我在项目中同时拥有 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)
...

The bean connectionFactoryTransaction manager is defined like this in the Spring class R2dbcTransactionManagerAutoConfiguration: bean connectionFactoryTransaction 管理器在 Spring 类 R2dbcTransactionManagerAutoConfiguration 中定义如下:

    @Bean
    @ConditionalOnMissingBean(ReactiveTransactionManager.class)
    public R2dbcTransactionManager connectionFactoryTransactionManager(ConnectionFactory connectionFactory) {
        return new R2dbcTransactionManager(connectionFactory);
    }

The bean transactionManager is defined like this in the Spring class DataSourceTransactionManagerAutoConfiguration: bean transactionManager 在 Spring 类 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;
   }

As can be seen, the @ConditionalOnMissingBean annotation contains different types which will cause an instance of both beans to be created.可以看出,@ConditionalOnMissingBean 注释包含不同的类型,这将导致创建两个 bean 的实例。 However, in the Spring class TransactionAspectSupport there is this line of code in the determineTransactionManager method:但是,在 Spring 类 TransactionAspectSupport 中,determineTransactionManager 方法中有这行代码:

defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);

Since both of the transaction manager types, DataSourceTransactionManager and R2dbcTransactionManager, implement the TransactionManager interface, both the transaction manager beans as above will be matched and the error will occur.由于事务管理器类型DataSourceTransactionManager 和R2dbcTransactionManager 都实现了TransactionManager 接口,因此上面的两个事务管理器bean 都会匹配,并且会发生错误。

I am now reaching out to hear if there is anyone who has managed to solve or work around this issue?我现在正在联系是否有人设法解决或解决了这个问题?
Thanks in advance!提前致谢!

With inspiration from M. Deinums answer (thanks!), I applied the following steps to my project and the test that failed earlier now runs successfully:受 M. Deinums 回答的启发(谢谢!),我将以下步骤应用于我的项目,之前失败的测试现在成功运行:

  • Remove the spring-boot-starter-jdbc dependency.删除 spring-boot-starter-jdbc 依赖项。
  • Add a dependency to spring-jdbc.向 spring-jdbc 添加依赖项。
  • Add a dependency to HikariCP (com.zaxxer).添加对 HikariCP (com.zaxxer) 的依赖。
  • Add spring.liquibase user and password properties (I already had the url and change-log properties).添加 spring.liquibase 用户和密码属性(我已经有了 url 和更改日志属性)。
  • Remove all spring.datasource properties (I had url and drive-class-name).删除所有 spring.datasource 属性(我有 url 和 drive-class-name)。

I had the spring.r2dbc properties username, password and url defined which I did not need to change.我定义了 spring.r2dbc 属性用户名、密码和 url,我不需要更改。

Update:更新:
In addition, I used Testcontainers in the tests and could not assign a static port.另外,我在测试中使用了Testcontainers,无法分配静态端口。 In order to be able to configure the database port on Liquibase, I overrode a bean name liquibase of the type SpringLiquibase and created a DataSource (not exposed as a bean) in the liquibase bean creation method and set it on the liquibase bean.为了能够在 Liquibase 上配置数据库端口,我覆盖了一个类型为 SpringLiquibase 的 bean 名称 liquibase 并在 liquibase bean 创建方法中创建了一个 DataSource(不公开为 bean)并将其设置在 liquibase bean 上。

It's possible to have spring-boot-starter-jdbc and spring-boot-starter-data-r2dbc co-exist. spring-boot-starter-jdbcspring-boot-starter-data-r2dbc共存。 There is a class org.springframework.transaction.annotation.TransactionManagementConfigurer that can be used to resolve the conflict.有一个类org.springframework.transaction.annotation.TransactionManagementConfigurer可用于解决冲突。 Spring Boot 2.3.0 seems to disable automatic datasource config when r2dbc is present.当 r2dbc 存在时,Spring Boot 2.3.0 似乎禁用了自动数据源配置。 It's possible to manually import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration class to make both co-exist.可以手动导入org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类以使两者共存。

@Bean
TransactionManagementConfigurer transactionManagementConfigurer(ReactiveTransactionManager reactiveTransactionManager) {
    return new TransactionManagementConfigurer() {
        @Override
        public TransactionManager annotationDrivenTransactionManager() {
            return reactiveTransactionManager;
        }
    };
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM