【问题标题】:Choose between muliple transaction managers at runtime在运行时在多个事务管理器之间进行选择
【发布时间】:2014-05-18 13:25:24
【问题描述】:

我有 2 个客户端使用部署在 tomcat 中的相同基于 Spring 的 REST 应用程序。根据客户,我需要在数据源和事务管理器之间进行选择。如何在运行时选择使用哪个事务管理器?

    <bean id="First_dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="url" value="${First_jdbc.url}" />
            <property name="driverClassName" value="${First_jdbc.driverClassName}" />
            <property name="username" value="${First_jdbc.username}" />
            <property name="password" value="${First_jdbc.password}" />
            <property name="removeAbandoned" value="true" />
            <property name="initialSize" value="20" />
            <property name="maxActive" value="30" />
            <!-- <property name="defaultAutoCommit" value="false" /> -->
   </bean>

    <bean id="Second_dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
            <property name="url" value="${Second_jdbc.url}" />
            <property name="driverClassName" value="${Second_jdbc.driverClassName}" />
            <property name="username" value="${Second_jdbc.username}" />
            <property name="password" value="${Second_jdbc.password}" />
            <property name="removeAbandoned" value="true" />
            <property name="initialSize" value="20" />
            <property name="maxActive" value="30" />
            <!-- <property name="defaultAutoCommit" value="false" /> -->
   </bean>

<bean id="First_TransactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        scope="singleton">
        <property name="dataSource" ref="First_dataSource" />
        <qualifier value="SC_TM"></qualifier>
</bean>

<bean id="Second_TransactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          scope="singleton">
          <property name="dataSource" ref="Second_dataSource" />
          <qualifier value="Second_TM"></qualifier>
</bean>   

在代码中如何在运行时选择@Transactional("????")。 如果 org.springframework.jdbc.datasource.DataSourceTransactionManager 不可能,还有其他方法吗?

【问题讨论】:

    标签: spring runtime transactionmanager


    【解决方案1】:

    使用@Transactional,您可以像这样指定事务管理器:

    @Transactional("First_TransactionManager")
    

    @Transactional("Second_TransactionManager")
    

    取决于您要使用哪一个。确保在事务方法中使用正确的实体管理器/会话工厂。那些你还必须指定你想用@PersistenceContext("nameOfPersistenceUnit")注入哪一个。

    【讨论】:

    • 感谢您的回复。我知道您已经回答了 @Transactional("First_TransactionManager") 我们可以选择的内容。但我不想硬编码。你能参考上面的例子举一个@PersistenceContext的例子吗
    【解决方案2】:

    我不知道你为什么要在 2 个事务管理器之间进行更改可能是你需要检查事务管理器链解决方案,但如果你需要这个,你可以在 Repo 方法上添加你的 @transactional 并执行 2 个 Repos 并管理它从服务层作为开关,否则我相信可以使用 AOP 完成解决方案,但需要更多时间考虑。

    【讨论】:

    • 如前所述,我为两个不同的客户端使用相同的源代码。根据 orgID,我需要选择特定的数据库及其相应的事务管理器。我尝试按照您通过 AOP 的建议进行操作,但我怀疑它是否真的设置了事务。出于测试目的,我在执行插入记录的存储过程后故意抛出了一个 RunTimeException。但是我的交易没有回滚。我会在此之后发布代码。
    • 让我为您推荐另一个解决方案,如果您了解代理设计模式,为什么不在需要插入数据库时​​调用代理,在此代理中检查 orgID,并基于它您可以为每个组织调用不同的存储库,并且在这些存储库中您可以提及事务管理器名称,它可以解决您的问题吗??
    【解决方案3】:

    问题通过AOP解决。

    1. 定义多个数据源和相应的事务管理器(正如我在我的 基本问题)
    2. First_dataSource 映射到 First_TransactionManager 和 Second_dataSource 映射到 Second_TransactionManager
    3. 根据您的业务规则选择以编程方式使用的数据源。就我而言,它是 orgId

      公共类 DataSourceProvider { @自动连线 数据源 First_dataSource; @自动连线 数据源Second_dataSource;

          public DataSource getDataSource(int orgId) {
      
              if (orgId == Constants.BUSINESS_PARTY_1)
                  return Second_dataSource;
              else
                  return First_dataSource;
          }
      
          public DataSource getFirst_dataSource() {
              return First_dataSource;
          }
      
          public void setFirst_dataSource(DataSource First_dataSource) {
              First_dataSource = First_dataSource;
          }
      
          public DataSource getSecond_dataSource() {
              return Second_dataSource;
          }
      
          public void setSecond_dataSource(DataSource Second_dataSource) {
              Second_dataSource = Second_dataSource;
          }
      }
      
    4. AOP 配置:

    【讨论】:

      【解决方案4】:
      <tx:advice id="First_txAdvice" transaction-manager="First_TransactionManager">
          <tx:attributes>
              <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />
          </tx:attributes>
      </tx:advice>
      
      <aop:config>
          <aop:pointcut id="First_daoimplOperation"
              expression="execution(* in.company.common.service.CommonServiceImpl.*(..))" />
      
          <aop:advisor advice-ref="First_txAdvice" pointcut-ref="First_daoimplOperation" />
      </aop:config>
      
      <tx:advice id="Second_txAdvice" transaction-manager="Second_TransactionManager">
          <tx:attributes>
              <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />
          </tx:attributes>
      </tx:advice>
      
      <aop:config>
          <aop:pointcut id="Second_daoimplOperation"
              expression="execution(* in.company.common.service.CommonServiceImpl.*(..))" />
      
          <aop:advisor advice-ref="Second_txAdvice" pointcut-ref="Second_daoimplOperation" />
      </aop:config>
      

      所有与数据库相关的服务都应该在匹配的切入点中,例如:in.company.common.service.CommonServiceImpl.*(..))

      【讨论】:

        【解决方案5】:

        考虑使用 Spring 提供的 AbstractRoutingDataSource,而不是在多个事务管理器之间进行选择。这将是一个更清洁的解决方案。

        在此处查看我对类似问题的回答:
        https://stackoverflow.com/a/44167079/2200690

        【讨论】:

          猜你喜欢
          • 2015-08-23
          • 2016-09-04
          • 2021-02-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-13
          相关资源
          最近更新 更多