一文讓你搞懂Spring的統一事務模型

Spring事務的知識體系

進入主題之前,先來了解一下Spring事務,都有哪些內容:

一文讓你搞懂Spring的統一事務模型

Spring事務包含對分佈式事務單機事務的支持,我們用的比較多的是單機事務,也就是隻操作一個數據庫的事務。

單機事務,按照用法分,又可以分爲編程式事務模型(TransactionTemplate)和聲明式事務模型(@Transactional註解),後者可以理解爲 aop + 編程式事務模型。

編程式事務模型裏面涉及到很多知識點,比如統一事務模型、事務傳播級別、事務隔離級別等。

我們今天要講的是其中一點,統一事務模型

希望這次的分享能夠讓大家,對Spring事務有一個整體性的認識。

不僅僅是Template

Spring的統一事務模型,解決的一個核心問題,就是不管你用的是什麼數據訪問方式,Hibernate、MyBatis抑或是JDBC,你的Service層的代碼都是一樣的,不需要做任何變動。

使用@Transactional註解的,相信大家都用過,而且由於註解的實現比較隱晦,不利於我們理解原理,這裏就不演示。

下面介紹編程式事務模型,TransactionTemplate

一文讓你搞懂Spring的統一事務模型

不管後面你的Dao實現如何變化,上面這一段Service代碼都無需修改,而且依舊可以保持事務的邏輯。

Spring是怎麼做到的呢?

有人說,是模板模式

點開TransactionTemplate,的確是封裝了事務操作的“套路”:

一文讓你搞懂Spring的統一事務模型

但是細看就會發現,這和我們傳統的template模式還有點不同。

傳統的template,一般會有一個抽象類,抽象類裏封裝了一系列有規律的套路,然後有些套路是抽象的,需要你自己去實現:

一文讓你搞懂Spring的統一事務模型

而TransactionTemplate,它已經是一個具體的類,無需實現任何方法,拿來即用。

但仔細看,就會發現裏面有一個叫transactionManager的傢伙,出鏡率特別高,它幫TransactionTemplate做了很多事情。

點開一看,這傢伙是個叫PlatformTransactionManager的接口:

一文讓你搞懂Spring的統一事務模型

恍然大悟,你只需給TransactionTemplate傳一個PlatformTransactionManager的具體實現,也就是告訴TransactionTemplate,事務創建、提交、回滾的具體策略,它就可以按照自己的那套流程,完成事務的操作。

TransactionTemplate,其實是模板+策略的雙劍合璧。

針對不同的廠商,只需要提供不同的PlatformTransactionManager實現即可。

比如對於MyBatis,就用DataSourceTxManager,對於Hibernate,就用HibernateTxManager:

一文讓你搞懂Spring的統一事務模型

不同廠商在實現的時候,按照自己對應的事務操作方式,進行實現即可。

比如DataSourceTxManager,創建事務的時候,new了一個自己的事務對象,最後返回一個Object類型,在commit的時候,再把這個Object,強轉成自己的事務對象:

一文讓你搞懂Spring的統一事務模型

HibernateTxManager也是如此:

一文讓你搞懂Spring的統一事務模型

我們在使用的時候,只需要通過Spring IOC,告訴Spring,要注入哪個TransactionManager,要使用哪種策略即可:

一文讓你搞懂Spring的統一事務模型

connection-pass

瞭解完Spring是如何實現統一的事務模型,不知道你是否也有疑問:既然是事務,那就要保證事務裏的所有dao操作,都要使用同一個數據庫連接進行操作,但是我們在寫代碼的時候,並不需要給dao傳入connection對象:

一文讓你搞懂Spring的統一事務模型

Spring又是怎麼做到的?

答案是ThreadLocal

通過ThreadLocal,在同一個線程中共享connection。

這很好理解,關鍵是,這是一個什麼樣的ThreadLocal?填空題。

一文讓你搞懂Spring的統一事務模型

也許你和我一開始想的一樣,認爲這裏面放到就是connection對象。

直接放connection對象會有一個問題,那就是當你事務裏面,涉及到對多個數據庫進行操作時,後面的操作取到的,就都是第一個數據庫操作放進去的connection:

一文讓你搞懂Spring的統一事務模型

如上圖,假設deleteAll操作的是db1,那麼它創建了針對db1的connection,然後放進ThreadLocal,然後save,本來是想操作db2的,結果它從threadLocal裏拿到的,卻是剛剛deleteAll時,放進去的操作db1的connection,卒。

實際上,Spring在ThreadLocal裏頭,放的是一個Map。key是dataSource,value纔是connection.

一文讓你搞懂Spring的統一事務模型

如何新開一個事務

Spring是支持在事務裏面新開一個事務的,最簡單的方式就是使用聲明式事務模型:

一文讓你搞懂Spring的統一事務模型

然而,按照之前的理論,如果每次都是從ThreadLocal裏去獲取connection,那麼永遠拿到的都是舊的事務,不會創建新事務。

Spring又是如何實現新開事務的呢?

很簡單,鏈表。

一開始,舊事務綁定在當前線程:

一文讓你搞懂Spring的統一事務模型

當需要新開事務時,先將原來的事務解綁:

一文讓你搞懂Spring的統一事務模型

然後new一個新的事務:

一文讓你搞懂Spring的統一事務模型

接着將新的事務指向舊事務:

一文讓你搞懂Spring的統一事務模型

最後將新事務綁定到當前線程:

一文讓你搞懂Spring的統一事務模型

之所以需要將新事務指向舊事務,形成一個事務鏈,是因爲新事務在提交或者回滾之後,還需要恢復舊事務:

一文讓你搞懂Spring的統一事務模型

一文讓你搞懂Spring的統一事務模型

這一塊邏輯對應的代碼:

一文讓你搞懂Spring的統一事務模型

一文讓你搞懂Spring的統一事務模型

總結

  • Spring如何實現統一的事務模型:Template + Strategy
  • 如何在方法間共享Connection:ThreadLocal
  • 如何掛起和恢復線程:鏈表
  • 提到的類:
    • TransactionTemplate 事務模板
    • PlatformTransactionManager 事務操作策略接口
    • AbstractPlatformTransactionManager 事務操作策略抽象類
      • DataSourceTxManager 具體策略,適用於JDBC/MyBatis
      • HibernateTxManager 具體策略,適用於Hibernate
    • TxSynManager 事務同步管理器,在線程中同步數據庫連接等信息
    • DataSourceUtils 數據庫操作Utils
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章