這一章主要介紹,分佈式事務的雛形-spring事務在多庫下的處理。這個算是比較可貴而且很容易在開發過程當中遇到各類坑。介紹以前先講一下問題的原因,前一陣,一個朋友說他在作一個業務,須要先從A庫撈取一些數據,而後再B庫裏面根據對A庫數據的處理來決定是否插入一條數據,在測試的時候測了回滾的狀況,可是死活回滾不成功,後來在架構師的幫助下解決了。解決方法是重新配置一個事務管理器,這個東西引發了個人注意,由於我以爲若是在開事務的時候先切數據源應該不會這麼麻煩,帶着這個問題來探討。spring
要清楚地看到問題,就必須看一下spring怎麼保證從事務中獲取的連接是同一個。核心其實就是一個線程局部變量,而後放了一個map,map的key : datasource,value:connection包裝類,至於這麼作的緣由:spring保證多個數據源依然能夠在事務中準確獲取連接。sql
那回到那個問題,切數據源爲何回滾無效?假設不在@transactional(value="xxx")配置事務管理器,那麼事務管理器一開始會用的默認數據源。相似下圖。那麼此時在取連接的時候,會看下當前是否存在事務,不存在直接提交,若是存在,那麼會拿到datasource,從map中取連接,可是很遺憾,此時的數據源已經不是事務管理器中的那個數據源了,因此取不到。若是配置了事務管理器,那麼一切就好辦了。編程
bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 數據源 dataSource在applicationContext-dao.xml中配置了 --> <property name="dataSource" ref="dataSource"/> </bean>
帶着這個問題,假設場景變一下,有A庫和B庫,在A,B庫同時插入數據,有一個出現異常就會滾,怎麼解決?解決方案看似不少,可是都和事務管理器有關。在探討這個問題的時候也讓我一直存在疑惑的究竟是用mybatis的接口好仍是原生dao好,在解決分佈式事務問題,最好用編程式事務+原生dao,否則好多錯根本跟蹤不到,直接上代碼。session
public class CommonSqlsession { private SqlSession sqlSession; @Resource(name = "sqlSessionFactory") public final void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } public final SqlSession getSqlSession() { return this.sqlSession; } }
@Repository public class UserDaoImpl extends CommonSqlsession implements UserDao { public int insert(User user) throws Exception { return getSqlSession().insert("com.elin4it.ssm.mapper.mybatis.UserMapper.insert", user); } }
@Repository("seoFundDao") public class SeoFundDaoImpl extends CommonSqlsession1 implements SeoFundDao { public int insert(TbFundSeoRecord seoRecord) throws Exception { return getSqlSession().insert("com.elin4it.ssm.mapper.test.TbFundSeoRecordMapper.insert", seoRecord); } }
@Resource private DataSourceTransactionManager transactionManager; @Resource private DataSourceTransactionManager transactionManager1;
/** * @param record * @param user * @return */ public int insertFacade1(TbFundSeoRecord record, User user) { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = transactionManager.getTransaction(def); TransactionStatus status1 = transactionManager1.getTransaction(def); try { int count1 = userDao.insert(user); // int i = 1 / 0; int count2 = seoFundDao.insert(record); if (count1 > 0 && count2 > 0) { transactionManager.commit(status); transactionManager1.commit(status1); } else { throw new Exception("有一個未提交成功 count1=" + count1 + ",count2=" + count2); } } catch (Exception e) { e.printStackTrace(); /** * spring提交事務按照stack排序,先入後出 */ transactionManager1.rollback(status1); transactionManager.rollback(status); } return 0; }
總的思路是這樣的,兩個事務管理器,而後從不一樣數據源取session,這樣spring在處理事務的時候會創建一個map在ThreadLocal中,而後設置兩個key,分別爲ds1 : con1,ds2:con2,可是這真的是能夠實現分佈式事務嗎?mybatis
經過上面兩個圖,能夠發現,若是在commit或者rollback的時候出問題,假設第一個事務提交成功,第二個事務提交的時候宕機了,那麼就會致使第一個庫中數據落地,第二個庫沒有數據。雖然commit很快,可是依然無法100%保證分佈式事務提交。因此真的在工做中要操做多庫的提交一致性仍是藉助JTA這種分佈式框架利用2PC來解決吧或者經過業務分類,保證一致性提交的表都在一個庫中。架構