Spring主从数据库切换,事务失效
2018-05-31 本文已影响96人
MC_Honva
前言
多数据库切换时候,存在事务,导致数据库切换失败,读写分离无法完成。其原因在于DataSourceTransactionManager类中,事务处理的方式,摘取部分源码,如下:
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
.......
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
txObject.getConnectionHolder().setTransactionActive(true);
.......
}
因为建立数据源链接在一些连接驱动中是非常耗时的处理,所以为了避免这样的操作,在事务处理的时候,会先把数据源放入缓存中,等待事务处理完成才会清除缓存,所以如果在service层中的方法中添加了事务,并且方法中存在切换数据源的操作,将会导致切换失败。情景如下:
@Transactional
public Integer updateType(TypeDto typeDto) {
....
TypeModle oldTypeModle = typeDao.findById(typeDto.getId());
....
return this.enumTypeDao.updateById(oldtypeModle);
}
解决方法及思路
-
思路
在获取事务锁定数据源之前切换数据源。 -
方法一
将事务处理放在DAO层,切面放在SERVICE层。
//Service层
@Before("execution(* cn.ymanager.service..*.*(..))")
public void dbAspect(JoinPoint point){
.....
}
//DAO层
@Transactional
public interface Dao extends BaseDao<Modle, Long> {
...........
}
- 方法二
如上所说,因为切换数据源的时候,事务先执行了,导致切换数据库失败。也可以从事务方法入手。在DataSourceTransactionManager类的dobegin方法中修改。
创建自己的类,继承DataSourceTransactionManage,重写dobegin方法。因为dobegin方法是在类AbstractRoutingDataSource的determineCurrentLookupKey()前执行。
public class SpayTransactionManager extends DataSourceTransactionManager {
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
...
//写入自己的逻辑判断,并且切换到自己想到数据库
super.doBegin(transaction, definition);
}
}
关于主从数据库切换可参考此篇文章
SpringMVC主从数据库切换