记一个最近遇到的问题,等后面了解springboot中事务管理的源码之后会来更新详细说明
· 关于springboot中多数据源的配置,查看之前的文章
项目场景:
由于最近做的项目中遇到多数据源的业务场景,就在思考一个问题,如果一个操作里面包含了多个数据库操作,并且这些数据库操作的数据源不止一个。需要使用事务来,保持数据一致性。
问题描述:
为了能够使得多个数据库操作能保持数据一致,就会使用到事务,但是如果使用springboot的@Transitional注解来做事务会报错
@Override
@Transactional(rollbackFor = Exception.class)
public void testData(){
mysqlDataMapper.updateTest();//操作数据库A
syncDataDao.updateTest();//操作数据库B
throw new RuntimeException();
}
错误信息:
No qualifying bean of type ‘org.springframework.transaction.PlatformTransactionManager’ available: more than one ‘primary’ bean found among candidates: [mysqlTransactionManager, oracleTransactionManager]
原因分析:
从提示信息可以看出,原因是因为配置了多个数据源,存在多个事务管理器PlatformTransactionManager解决方案:
可以在@Transitional中添加事务属性,指定事务管理器:
transactionManager = “mysqlTransactionManager”
但是这样只能对指定的数据源启动事务。其他有待研究方案
下面是最近想到的一个解决方案:
@Transactional(rollbackFor = Exception.class ,transactionManager = "aTransactionManager")
public void testA() throws Exception {
/**
* 执行A数据源的数据库操作
*/
//---其他业务逻辑
testB();//---把B数据源的数据库操作放在最后
}
@Transactional(rollbackFor = Exception.class ,transactionManager = "bTransactionManager")
public void testB() throws Exception {
/**
* 执行B数据源的数据库操作
*/
throw new Exception("ddd");
// 当B操作失败时,会回滚数据库操作。报出异常
}
按照上面代码逻辑去写就可以保证双数据源的时候事务一致性。分析如下:
- 第一种情况当操作A数据源的时候发生错误,错误会被testA上面的Transactional注解捕获,回滚A数据源的操作。根据java的执行顺序,当前面逻辑报错之后就不会执行后面的代码逻辑了。所以,testB是数据操作不会被执行。
- 第二种情况就是操作数据源A的时候没有发生错误,在testB中操作数据源B的时候发生了错误,那么错误信息会被testB上面的Transactional注解捕获,从而回滚数据库操作。这时,将异常传递到testA方法内,同样会被testA上的Transactional注解捕获,从而回滚A数据源的数据库操作。达到事务的一致性。