前言
多數(shù)據(jù)庫切換時候,存在事務(wù)十电,導(dǎo)致數(shù)據(jù)庫切換失敗知押,讀寫分離無法完成。其原因在于DataSourceTransactionManager類中鹃骂,事務(wù)處理的方式台盯,摘取部分源碼,如下:
@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);
.......
}
因為建立數(shù)據(jù)源鏈接在一些連接驅(qū)動中是非常耗時的處理畏线,所以為了避免這樣的操作静盅,在事務(wù)處理的時候,會先把數(shù)據(jù)源放入緩存中象踊,等待事務(wù)處理完成才會清除緩存温亲,所以如果在service層中的方法中添加了事務(wù)棚壁,并且方法中存在切換數(shù)據(jù)源的操作杯矩,將會導(dǎo)致切換失敗。情景如下:
@Transactional
public Integer updateType(TypeDto typeDto) {
....
TypeModle oldTypeModle = typeDao.findById(typeDto.getId());
....
return this.enumTypeDao.updateById(oldtypeModle);
}
解決方法及思路
思路
在獲取事務(wù)鎖定數(shù)據(jù)源之前切換數(shù)據(jù)源袖外。方法一
將事務(wù)處理放在DAO層史隆,切面放在SERVICE層。
//Service層
@Before("execution(* cn.ymanager.service..*.*(..))")
public void dbAspect(JoinPoint point){
.....
}
//DAO層
@Transactional
public interface Dao extends BaseDao<Modle, Long> {
...........
}
- 方法二
如上所說曼验,因為切換數(shù)據(jù)源的時候泌射,事務(wù)先執(zhí)行了,導(dǎo)致切換數(shù)據(jù)庫失敗鬓照。也可以從事務(wù)方法入手熔酷。在DataSourceTransactionManager類的dobegin方法中修改。
創(chuàng)建自己的類豺裆,繼承DataSourceTransactionManage拒秘,重寫dobegin方法。因為dobegin方法是在類AbstractRoutingDataSource的determineCurrentLookupKey()前執(zhí)行臭猜。
public class SpayTransactionManager extends DataSourceTransactionManager {
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
...
//寫入自己的邏輯判斷躺酒,并且切換到自己想到數(shù)據(jù)庫
super.doBegin(transaction, definition);
}
}
關(guān)于主從數(shù)據(jù)庫切換可參考此篇文章
SpringMVC主從數(shù)據(jù)庫切換