【问题标题】:Proper way to set up transactions in Spring for different data sources?在 Spring 中为不同数据源设置事务的正确方法?
【发布时间】:2009-09-24 16:08:31
【问题描述】:

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

基本上,应用程序的一个区域允许您在数据库 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>

这会为CompanyService 中任何方法的任何执行设置一个切入点,并将事务建议与需要对任何名称以“create”开头的方法进行事务处理的切入点相关联。事务通知与绑定到数据源的 TransactionManager 相关联。

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

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

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

我是不是走错了路?

更新:我实际上已经测试了我上面谈到的配置(将两个顾问附加到同一个切入点),并在 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...]

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

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

【问题讨论】:

    标签: java spring transactions


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢,这很好用——在调用任一 bean 实例时只创建一个事务。不像在界面上声明切入点那样优雅的解决方案,但这就是我的用例所要求的......
    【解决方案2】:

    您是否尝试过使用 JtaTransactionManager?

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

    【讨论】:

      猜你喜欢
      • 2011-01-21
      • 2022-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-29
      • 1970-01-01
      • 2015-06-08
      • 1970-01-01
      相关资源
      最近更新 更多