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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1472688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 16:40:21  来源:igfitidea点击:

Proper way to set up transactions in Spring for different data sources?

javaspringtransactions

提问by matt b

I have an application which needs to connect to multiple databases. This is an administrative application which is basically used to manage entries in different databases - we do not need to access multiple databases simultaneously nor do we need any sort of distributed transaction management.

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

Basically one area of the application lets you create gadgets in Database A, and another area of the application lets you configure similar gadgets in Database B.

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

We already have transactions set up and working perfectly when using just one datasource. The configuration looks like so:

当仅使用一个数据源时,我们已经设置了事务并完美运行。配置如下所示:

<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>

This sets a pointcut on any execution of any methods within CompanyServiceand associates transaction advice with the pointcut which requires transactions for any methods whose name starts with "create". The transaction advice is associated with a TransactionManager which is tied to the dataSource.

这会在任何方法的任何执行上设置切入点CompanyService,并将事务建议与切入点相关联,切入点需要名称以“create”开头的任何方法的事务。事务建议与绑定到数据源的 TransactionManager 相关联。

When adding a second (or more) datasources, how can I apply the same transaction advice to other datasources? Since the AOP advice can only be associated with one transactionManager, which can only be associated with one dataSource, do I need to set up duplicate transaction advice?

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

If I setup duplicate transaction advice to the same pointcut, won't this mean that any invocations of methods in my CompanyServiceinterface will require propogation against allof my dataSources?

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

To make my last question a little clearer, I will have multiple beans declared which implement the CompanyServiceinterface, and each of these beans will have a separate CompanyDAOto access their individual DataSource. I fear that this approach will mean that when the companyService1bean is invoked, transaction advice will be triggered on allcompanyService beans/dataSources.

为了让我的最后一个问题更清楚一点,我将声明多个实现CompanyService接口的 bean,并且这些 bean 中的每一个都将有一个单独的CompanyDAO用于访问它们各自的数据源。我担心这种方法意味着当companyService1调用 bean时,将在allcompanyService beans/dataSources上触发事务建议。

Am I going about this the wrong way?

我会以错误的方式解决这个问题吗?

Update:I've actually tested out the configuration I talked about above (attaching two advisors to the same pointcut), and invoking any method on either individual instance of the CompanyServiceimplementation does in fact create new transactions on both dataSources, as expected:

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

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...]

This seems like it would cause issues down the road, since either CompanyServiceinstance is only ever working with a single DataSource.

这似乎会导致问题,因为任何一个CompanyService实例都只使用单个数据源。

Is there a better way to configure what I seek to accomplish?

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

采纳答案by Chin Huang

Yes, you need a duplicate transaction advice. Notice in the following configuration that the pointcut expression selects a specific CompanyService bean.

是的,您需要重复的交易建议。请注意,在以下配置中,切入点表达式选择特定的 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>

To configure another CompanyService bean, you need to duplicate the same verbose boilerplate. Another way to demarcate transactions in Spring uses TransactionProxyFactoryBean. It's slighty less verbose because it uses a parent bean definition to configure common properties inherited by child beans.

要配置另一个 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>

回答by Gus

Have you tried using the JtaTransactionManager?

您是否尝试过使用 JtaTransactionManager?

http://forum.springsource.org/showthread.php?t=10476

http://forum.springsource.org/showthread.php?t=10476