Spring @Transactional 与跨多个数据源的事务

Posted

技术标签:

【中文标题】Spring @Transactional 与跨多个数据源的事务【英文标题】:Spring @Transactional with a transaction across multiple data sources 【发布时间】:2018-08-03 22:01:39 【问题描述】:

作为一个事务的一部分,我必须更新两个数据源。那就是——

    我在 DB1 中进行了更新。 然后,我在 DB2 中进行另一次更新。

如果 DB2 中的更新失败,我想回滚 DB1 和 DB2 以回滚。这可以使用@Transactional 来完成吗?

这是一个示例代码 -

@Transactional(value="db01TransactionManager")
public void updateDb01() 
    Entity01 entity01 = repository01.findOne(1234);
    entity01.setName("Name");
    repository01.save(entity01);

    //Calling method to update DB02
    updateDb02();


@Transactional(value="db02TransactionManager")
public void updateDb02() 
    Entity02 entity02 = repository02.findOne(1234);
    entity02.setName("Name");
    repository02.save(entity02);

    //Added this to force a roll back for testing
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

我的问题是,updateDb02 中的 setRollbackOnly() 只回滚 Db01 事务。

【问题讨论】:

您需要一个支持 XA 的事务管理器.. 【参考方案1】:

我已经使用 ChainedTransactionManager 解决了这个问题 - http://docs.spring.io/spring-data/commons/docs/1.6.2.RELEASE/api/org/springframework/data/transaction/ChainedTransactionManager.html

Spring Boot 配置:

    @Bean(name = "chainedTransactionManager")
    public ChainedTransactionManager transactionManager(@Qualifier("primaryDs") PlatformTransactionManager ds1,
                                                    @Qualifier("secondaryDs") PlatformTransactionManager ds2) 
         return new ChainedTransactionManager(ds1, ds2);
    

然后你可以如下使用它:

@Transactional(value="chainedTransactionManager")
public void updateDb01() 
    Entity01 entity01 = repository01.findOne(1234);
    entity01.setName("Name");
    repository01.save(entity01);

    //Calling method to update DB02
    updateDb02();


public void updateDb02() 
    Entity02 entity02 = repository02.findOne(1234);
    entity02.setName("Name");
    repository02.save(entity02);

    //Added this to force a roll back for testing
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

【讨论】:

ChainedTransactionManager 对我有用。我必须处理 4 个数据库,这是唯一可以帮助我的事情 为了我未来的自己和其他人:如果您希望@Transactional 默认使用ChainedTransactionManager,请在ChainedTransactionManager bean 上添加@Primary 正是我的问题,正是应用的正确解决方案......谢谢! 这是我写的一篇文章,说明了如何使用一些更详细的代码示例来设置链式事务管理器。这可能有助于解释所有组件如何协同工作以产生此结果:metamorphant.de/blog/posts/… 请注意,ChainedTransactionManager 由于其意外行为已被弃用。请参阅此ticket 了解更多信息。【参考方案2】:

最好的方法是创建第三种方法,将其注释为@Transactional

@Transactional(readOnly = false)
public void updateCommon()
  upbateDb01();
  upbateDb02();

根据 spring 文档,事务控制在出现第一个注释时启动,因此在这种情况下,将在调用 updateCommon 时启动单个事务。 更新 但是,如果您使用 CrudRepository 或类似的东西,这将起作用。

如果有多个数据源,您可以尝试使用全局事务管理概念。以下是来自 spring 文档的示例:

@Inject private PlatformTransactionManager txManager; 

TransactionTemplate template  = new TransactionTemplate(this.txManager); 
template.execute( new TransactionCallback<Object>() 
  public void doInTransaction(TransactionStatus status) 
   // work done here will be wrapped by a transaction and committed. 
   // the transaction will be rolled back if 
   // status.setRollbackOnly(true) is called or an exception is thrown 
   
);

这是一个链接:http://spring.io/blog/2011/08/15/configuring-spring-and-jta-without-full-java-ee/ 我从来没有为自己使用它,所以我没有深入探讨这个话题。 希望对你有帮助

【讨论】:

我的问题是,因为我有多个数据源,它会用这个@Transactional 启动一个事务管理器,并且只有那个可以参与事务。因此,使用您的解决方案,我仍然会遇到只有一个事务管理器适用于我的所有数据库更新的问题 该死,你说得对,不知怎的我错过了(我会根据写的条件更新我的答案【参考方案3】:

想通了。

方法必须位于不同的 bean 中才能使用不同的事务管理器。

【讨论】:

如果有不同的bean,我想你不会自己遇到问题。【参考方案4】:

我相信你已经像下面这样定义了你的 txns。

@Bean(name="db01TransactionManager") 
@Autowired
DataSourceTransactionManager tm1(@Qualifier ("datasource1") DataSource datasource) 
    DataSourceTransactionManager txm  = new DataSourceTransactionManager(datasource);
    return txm;


@Bean(name="db02TransactionManager") 
@Autowired
DataSourceTransactionManager tm2(@Qualifier ("datasource2") DataSource datasource) 
    DataSourceTransactionManager txm  = new DataSourceTransactionManager(datasource);
    return txm;

现在最简单的方法是对这两个事务进行尝试、捕获和回滚。但如果您仍想委托,可以选择如下所示。

创建您自己的并覆盖回滚方法并使用它。

@Bean(name=“allTransactionManager") 
@Autowired
DataSourceTransactionManager tm2(@Qualifier ("datasource1”) DataSource datasource1, @Qualifier ("datasource2") DataSource datasource2) 

    DataSourceTransactionManager txm  = new MyDataSourceTransactionManager(datasource1,datasouce2);
        return txm;

并将你自己的事务管理器定义为。

MyDataSourceTransactionManager extends DataSourceTransactionManager
DataSourceTransactionManager tm1; 
DataSourceTransactionManager tm2; 

MyDataSourceTransactionManager(Datasource ds1,Datasource d2)
  tm1 = new DataSourceTransactionManager(DataSource);
  tm2 =new DataSourceTransactionManager(DataSource);

// override and for roll back, rollback for both of tm1 and tm2. Thus all actions are delegated in this class


然后将它用于您想要同步工作的任何 dao 层。

 @Transactional("allTransactionManager")

所以现在我们有了您自己的事务管理器,它能够为两种类型的事务一起回滚或提交。

【讨论】:

看起来不错。 TS @Do Will,如果你想试试这个,请给我们反馈。

以上是关于Spring @Transactional 与跨多个数据源的事务的主要内容,如果未能解决你的问题,请参考以下文章

Grails @Transactional 与 Spring @Transactional 注释之间的差异

Spring - @Transactional - 在后台发生了啥?

Spring @Transactional 和 Spring @Lock 注解有啥关系?

Spring @Transactional 使用

Spring @Transactional 只读传播

Spring @Transactional 属性是不是适用于私有方法?