在Spring中为不同的数据源设置事务的正确方法是什么?

mat*_*t b 10 java spring transactions

我有一个需要连接到多个数据库的应用程序.这是一个管理应用程序,主要用于管理不同数据库中的条目 - 我们不需要同时访问多个数据库,也不需要任何类型的分布式事务管理.

基本上,应用程序的一个区域允许您在数据库A中创建小工具,而应用程序的另一个区域允许您在数据库B中配置类似的小工具.

当使用一个数据源时,我们已经设置了事务并且工作正常.配置如下所示:

<aop:config>
    <aop:pointcut id="companyServicePoint" 
          expression="execution(* com.company.service.CompanyService.*(..))" />

    <aop:advisor advice-ref="companyServiceTxAdvice"
         pointcut-ref="companyServicePoint"/>
</aop:config>

<tx:advice id="companyServiceTxAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- set propogation required on create methods, all others are read-only -->
        <tx:method name="create*" propagation="REQUIRED"/>
        <tx:method name="*" read-only="true" />
    </tx:attributes>
</tx:advice>

<bean id="txManager" 
   class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
Run Code Online (Sandbox Code Playgroud)

这为任何执行任何方法设置切入点CompanyService,并将事务通知与切入点相关联,切入点需要对名称以"create"开头的任何方法进行事务处理.事务通知与TransactionManager相关联,该TransactionManager与dataSource相关联.

添加第二个(或更多)数据源时,如何将相同的事务建议应用于其他数据源?由于AOP建议只能与一个只能与一个dataSource关联的transactionManager关联,我是否需要设置重复的事务建议?

如果我为同一个切入点设置重复的事务建议,这是否意味着我的CompanyService接口中的任何方法调用都需要对我的所有 dataSource 进行传播?

为了使我的上一个问题更加清晰,我将声明多个bean实现CompanyService接口,并且每个bean都有一个单独的bean CompanyDAO来访问它们各自的DataSource.我担心这种方法意味着当companyService1调用bean 时,将在allcompanyService beans/dataSources 上触发事务建议.

我是以错误的方式来做这件事的吗?

更新:我实际上已经测试了我上面谈到的配置(将两个顾问程序连接到同一个切入点),并且在CompanyService实现的任何单个实例上调用任何方法确实在两个dataSource上创建了新的事务,正如预期的那样:

DEBUG company.serviceDataSourceTransactionManager - Creating new transaction with name [com.company.service.CompanyService.createCompany]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG company.serviceDataSourceTransactionManager - Acquired Connection [connection1 string here...] for JDBC transaction
...
DEBUG company.serviceDataSourceTransactionManager - Creating new transaction with name [com.company.service.CompanyService.createCompany]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG company.serviceDataSourceTransactionManager - Acquired Connection [connection2 string here...] for JDBC transaction
...
DEBUG company.serviceDataSourceTransactionManager - Rolling back JDBC transaction on Connection [connection1 string here...]
...
DEBUG company.serviceDataSourceTransactionManager - Rolling back JDBC transaction on Connection [connection2 string here...]
Run Code Online (Sandbox Code Playgroud)

这似乎会导致问题,因为任何一个CompanyService实例只使用单个DataSource.

有没有更好的方法来配置我想要完成的任务?

Chi*_*ang 3

是的,您需要重复的交易通知。请注意,在以下配置中,切入点表达式选择特定的 CompanyService bean。

<bean id="companyService1" class="com.company.service.CompanyServiceImpl">
  <property name="companyDao">
    <bean class="com.company.service.CompanyDAO">
      <property name="dataSource" ref="dataSource1"/>
    </bean>
  </property>
</bean>

<aop:config>
  <aop:pointcut
      id="companyServicePoint1"
      expression="bean(companyService1)"/>
  <aop:advisor
      advice-ref="companyServiceTxAdvice1"
      pointcut-ref="companyServicePoint1"/>
</aop:config>

<tx:advice id="companyServiceTxAdvice1" transaction-manager="txManager1">
  <tx:attributes>
    <!-- set propogation required on create methods, all others are read-only -->
    <tx:method name="create*" propagation="REQUIRED"/>
    <tx:method name="*" read-only="true"/>
  </tx:attributes>
</tx:advice>

<bean
    id="txManager1" 
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource1"/>
</bean>
Run Code Online (Sandbox Code Playgroud)

要配置另一个 CompanyService bean,您需要复制相同的详细样板。Spring 中划分事务的另一种方法是使用TransactionProxyFactoryBean. 它稍微不那么冗长,因为它使用父 bean 定义来配置子 bean 继承的公共属性。

<bean
    id="baseTransactionProxy"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
    abstract="true">
  <property name="transactionAttributes">
    <props>
      <prop key="create*">PROPAGATION_REQUIRED</prop>
      <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
    </props>
  </property>
</bean>

<bean id="companyService1" parent="baseTransactionProxy">
  <property name="transactionManager" ref="txManager1"/>
  <property name="target">
    <bean class="com.company.service.CompanyServiceImpl">
      <property name="companyDao">
        <bean class="com.company.service.CompanyDAO">
          <property name="dataSource" ref="dataSource1"/>
        </bean>
      </property>
    </bean>
  </property>
</bean>

<bean
    id="txManager1" 
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource1"/>
</bean>
Run Code Online (Sandbox Code Playgroud)