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

Spring事務的知識體系

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

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

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

編程式事務模型裏面涉及到不少知識點,好比統一事務模型、事務傳播級別、事務隔離級別等。分佈式

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

但願此次的分享可以讓你們,對Spring事務有一個總體性的認識。3d

不只僅是Template

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

使用@Transactional註解的,相信你們都用過,並且因爲註解的實現比較隱晦,不利於咱們理解原理,這裏就不演示。對象

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

無論後面你的Dao實現如何變化,上面這一段Service代碼都無需修改,並且依舊能夠保持事務的邏輯。事務

Spring是怎麼作到的呢?

有人說,是模板模式

點開TransactionTemplate,的確是封裝了事務操做的「套路」:

可是細看就會發現,這和咱們傳統的template模式還有點不一樣。

傳統的template,通常會有一個抽象類,抽象類裏封裝了一系列有規律的套路,而後有些套路是抽象的,須要你本身去實現:

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

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

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

恍然大悟,你只需給TransactionTemplate傳一個PlatformTransactionManager的具體實現,也就是告訴TransactionTemplate,事務建立、提交、回滾的具體策略,它就能夠按照本身的那套流程,完成事務的操做。

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

針對不一樣的廠商,只須要提供不一樣的PlatformTransactionManager實現便可。

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

不一樣廠商在實現的時候,按照本身對應的事務操做方式,進行實現便可。

好比DataSourceTxManager,建立事務的時候,new了一個本身的事務對象,最後返回一個Object類型,在commit的時候,再把這個Object,強轉成本身的事務對象:

HibernateTxManager也是如此:

咱們在使用的時候,只須要經過Spring IOC,告訴Spring,要注入哪一個TransactionManager,要使用哪一種策略便可:

connection-pass

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

Spring又是怎麼作到的?

答案是ThreadLocal

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

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

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

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

如上圖,假設deleteAll操做的是db1,那麼它建立了針對db1的connection,而後放進ThreadLocal,而後save,原本是想操做db2的,結果它從threadLocal裏拿到的,倒是剛剛deleteAll時,放進去的操做db1的connection,卒。

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

如何新開一個事務

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

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

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

很簡單,鏈表。

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

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

而後new一個新的事務:

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

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

之因此須要將新事務指向舊事務,造成一個事務鏈,是由於新事務在提交或者回滾以後,還須要恢復舊事務:

這一塊邏輯對應的代碼:

總結

  • Spring如何實現統一的事務模型:Template + Strategy
  • 如何在方法間共享Connection:ThreadLocal
  • 如何掛起和恢復線程:鏈表
  • 提到的類:
    • TransactionTemplate 事務模板
    • PlatformTransactionManager 事務操做策略接口
    • AbstractPlatformTransactionManager 事務操做策略抽象類
      • DataSourceTxManager 具體策略,適用於JDBC/MyBatis
      • HibernateTxManager 具體策略,適用於Hibernate
    • TxSynManager 事務同步管理器,在線程中同步數據庫鏈接等信息
    • DataSourceUtils 數據庫操做Utils
相關文章
相關標籤/搜索