歡迎光臨
每天分享高質量文章

可能是最漂亮的 Spring 事務管理詳解

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 


摘要: 原創出處 https://juejin.im/post/5b00c52ef265da0b95276091 「Snailclimb」歡迎轉載,保留摘要,謝謝!

  • 事務概念回顧

  • Spring事務管理介面介紹

  • 666. 彩蛋


事務概念回顧

什麼是事務?

事務是邏輯上的一組操作,要麼都執行,要麼都不執行.

事物的特性(ACID):

事務的特性
  1. 原子性: 事務是最小的執行單位,不允許分割。事務的原子性確保動作要麼全部完成,要麼完全不起作用;

  2. 一致性: 執行事務前後,資料保持一致;

  3. 隔離性: 併發訪問資料庫時,一個使用者的事物不被其他事物所幹擾,各併發事務之間資料庫是獨立的;

  4. 永續性: 一個事務被提交之後。它對資料庫中資料的改變是持久的,即使資料庫發生故障也不應該對其有任何影響。

Spring事務管理介面介紹

Spring事務管理介面:

  • PlatformTransactionManager: (平臺)事務管理器

  • TransactionDefinition: 事務定義資訊(事務隔離級別、傳播行為、超時、只讀、回滾規則)

  • TransactionStatus: 事務執行狀態

所謂事務管理,其實就是“按照給定的事務規則來執行提交或者回滾操作”。

PlatformTransactionManager介面介紹

Spring並不直接管理事務,而是提供了多種事務管理器 ,他們將事務管理的職責委託給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。 Spring事務管理器的介面是: org.springframework.transaction.PlatformTransactionManager ,透過這個介面,Spring為各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,但是具體的實現就是各個平臺自己的事情了。

PlatformTransactionManager介面程式碼如下:

PlatformTransactionManager介面中定義了三個方法:

Public interface PlatformTransactionManager()...{
    // Return a currently active transaction or create a new one, according to the specified propagation behavior(根據指定的傳播行為,傳回當前活動的事務或建立一個新事務。)
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    // Commit the given transaction, with regard to its status(使用事務目前的狀態提交事務)
    Void commit(TransactionStatus status) throws TransactionException;
    // Perform a rollback of the given transaction(對執行的事務進行回滾)
    Void rollback(TransactionStatus status) throws TransactionException;
    }

我們剛剛也說了Spring中PlatformTransactionManager根據不同持久層框架所對應的介面實現類,幾個比較常見的如下圖所示

PlatformTransactionManager根據不同持久層框架所對應的介面實現

比如我們在使用JDBC或者iBatis(就是Mybatis)進行資料持久化操作時,我們的xml配置通常如下:

    
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

        
        <property name="dataSource" ref="dataSource" />
    bean>


TransactionDefinition介面介紹

事務管理器介面 PlatformTransactionManager 透過 getTransaction(TransactionDefinition definition) 方法來得到一個事務,這個方法裡面的引數是 TransactionDefinition類 ,這個類就定義了一些基本的事務屬性。

那麼什麼是事務屬性呢?

事務屬性可以理解成事務的一些基本配置,描述了事務策略如何應用到方法上。事務屬性包含了5個方面。

事務屬性

TransactionDefinition介面中的方法如下:

TransactionDefinition介面中定義了5個方法以及一些表示事務屬性的常量比如隔離級別、傳播行為等等的常量。

我下麵只是列出了TransactionDefinition介面中的方法而沒有給出介面中定義的常量,該介面中的常量資訊會在後面依次介紹到。

public interface TransactionDefinition {
    // 傳回事務的傳播行為
    int getPropagationBehavior();
    // 傳回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些資料
    int getIsolationLevel();
    // 傳回事務必須在多少秒內完成
    //傳回事務的名字
    String getName()
    int getTimeout()
;
    // 傳回是否最佳化為只讀事務。
    boolean isReadOnly();
}

(1)事務隔離級別(定義了一個事務可能受其他併發事務影響的程度):

我們先來看一下 併發事務帶來的問題 ,然後再來介紹一下 TransactionDefinition 介面 中定義了五個表示隔離級別的常量。

併發事務帶來的問題

在典型的應用程式中,多個事務併發執行,經常會操作相同的資料來完成各自的任務(多個使用者對統一資料進行操作)。併發雖然是必須的,但可能會導致一下的問題。

  • 臟讀(Dirty read): 當一個事務正在訪問資料並且對資料進行了修改,而這種修改還沒有提交到資料庫中,這時另外一個事務也訪問了這個資料,然後使用了這個資料。因為這個資料是還沒有提交的資料,那麼另外一個事務讀到的這個資料是“臟資料”,依據“臟資料”所做的操作可能是不正確的。

  • 丟失修改(Lost to modify): 指在一個事務讀取一個資料時,另外一個事務也訪問了該資料,那麼在第一個事務中修改了這個資料後,第二個事務也修改了這個資料。這樣第一個事務內的修改結果就被丟失,因此稱為丟失修改。

    例如:事務1讀取某表中的資料A=20,事務2也讀取A=20,事務1修改A=A-1,事務2也修改A=A-1,最終結果A=19,事務1的修改被丟失。

  • 不可重覆讀(Unrepeatableread): 指在一個事務內多次讀同一資料。在這個事務還沒有結束時,另一個事務也訪問該資料。那麼,在第一個事務中的兩次讀資料之間,由於第二個事務的修改導致第一個事務兩次讀取的資料可能不太一樣。這就發生了在一個事務內兩次讀到的資料是不一樣的情況,因此稱為不可重覆讀。

  • 幻讀(Phantom read): 幻讀與不可重覆讀類似。它發生在一個事務(T1)讀取了幾行資料,接著另一個併發事務(T2)插入了一些資料時。在隨後的查詢中,第一個事務(T1)就會發現多了一些原本不存在的記錄,就好像發生了幻覺一樣,所以稱為幻讀。

不可重覆度和幻讀區別:

不可重覆讀的重點是修改,幻讀的重點在於新增或者刪除。

例1(同樣的條件, 你讀取過的資料, 再次讀取出來發現值不一樣了 ):事務1中的A先生讀取自己的工資為 1000的操作還沒完成,事務2中的B先生就修改了A的工資為2000,導 致A再讀自己的工資時工資變為 2000;這就是不可重覆讀。

例2(同樣的條件, 第1次和第2次讀出來的記錄數不一樣 ):假某工資單表中工資大於3000的有4人,事務1讀取了所有工資大於3000的人,共查到4條記錄,這時事務2 又插入了一條工資大於3000的記錄,事務1再次讀取時查到的記錄就變為了5條,這樣就導致了幻讀。

隔離級別

TransactionDefinition 介面中定義了五個表示隔離級別的常量:

  • TransactionDefinition.ISOLATION_DEFAULT: 使用後端資料庫預設的隔離級別,Mysql 預設採用的 REPEATABLE_READ隔離級別 Oracle 預設採用的 READ_COMMITTED隔離級別.

  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔離級別,允許讀取尚未提交的資料變更,可能會導致臟讀、幻讀或不可重覆讀

  • TransactionDefinition.ISOLATION_READ_COMMITTED: 允許讀取併發事務已經提交的資料,可以阻止臟讀,但是幻讀或不可重覆讀仍有可能發生

  • TransactionDefinition.ISOLATION_REPEATABLE_READ: 對同一欄位的多次讀取結果都是一致的,除非資料是被本身事務自己所修改,可以阻止臟讀和不可重覆讀,但幻讀仍有可能發生。

  • TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生幹擾,也就是說,該級別可以防止臟讀、不可重覆讀以及幻讀。但是這將嚴重影響程式的效能。通常情況下也不會用到該級別。

是不是感覺腦細胞不夠用了,橋本奈奈未瞭解一下~~~顏值逆天好嗎!!!

橋本奈奈未

(2)事務傳播行為(為瞭解決業務層方法之間互相呼叫的事務問題):

當事務方法被另一個事務方法呼叫時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中執行,也可能開啟一個新事務,併在自己的事務中執行。在TransactionDefinition定義中包括瞭如下幾個表示傳播行為的常量:

支援當前事務的情況:

  • TransactionDefinition.PROPAGATION_REQUIRED: 如果當前存在事務,則加入該事務;如果當前沒有事務,則建立一個新的事務。

  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續執行。

  • TransactionDefinition.PROPAGATION_MANDATORY: 如果當前存在事務,則加入該事務;如果當前沒有事務,則丟擲異常。

不支援當前事務的情況:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW: 建立一個新的事務,如果當前存在事務,則把當前事務掛起。

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務方式執行,如果當前存在事務,則把當前事務掛起。

  • TransactionDefinition.PROPAGATION_NEVER: 以非事務方式執行,如果當前存在事務,則丟擲異常。

其他情況:

  • TransactionDefinition.PROPAGATION_NESTED: 如果當前存在事務,則建立一個事務作為當前事務的巢狀事務來執行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。

這裡需要指出的是,前面的六種事務傳播行為是 Spring 從 EJB 中引入的,他們共享相同的概念。而 PROPAGATION_NESTED 是 Spring 所特有的。以 PROPAGATION_NESTED 啟動的事務內嵌於外部事務中(如果存在外部事務的話),此時,內嵌事務並不是一個獨立的事務,它依賴於外部事務的存在,只有透過外部的事務提交,才能引起內部事務的提交,巢狀的子事務不能單獨提交。如果熟悉 JDBC 中的儲存點(SavePoint)的概念,那巢狀事務就很容易理解了,其實巢狀的子事務就是儲存點的一個應用,一個事務中可以包括多個儲存點,每一個巢狀子事務。另外,外部事務的回滾也會導致巢狀子事務的回滾。

(3) 事務超時屬性(一個事務允許執行的最長時間)

所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。

(4) 事務只讀屬性(對事物資源是否執行只讀操作)

事務的只讀屬性是指,對事務性資源進行只讀操作或者是讀寫操作。所謂事務性資源就是指那些被事務管理的資源,比如資料源、 JMS 資源,以及自定義的事務性資源等等。如果確定只對事務性資源進行只讀操作,那麼我們可以將事務標誌為只讀的,以提高事務處理的效能。在 TransactionDefinition 中以 boolean 型別來表示該事務是否只讀。

(5) 回滾規則(定義事務回滾規則)

這些規則定義了哪些異常會導致事務回滾而哪些不會。預設情況下,事務只有遇到執行期異常時才會回滾,而在遇到檢查型異常時不會回滾(這一行為與EJB的回滾行為是一致的)。 但是你可以宣告事務在遇到特定的檢查型異常時像遇到執行期異常那樣回滾。同樣,你還可以宣告事務遇到特定的異常不回滾,即使這些異常是執行期異常。

TransactionStatus介面介紹

TransactionStatus介面用來記錄事務的狀態 該介面定義了一組方法,用來獲取或判斷事務的相應狀態資訊.

PlatformTransactionManager.getTransaction(…) 方法傳回一個 TransactionStatus 物件。傳回的TransactionStatus 物件可能代表一個新的或已經存在的事務(如果在當前呼叫堆疊有一個符合條件的事務)。

TransactionStatus介面介面內容如下:

public interface TransactionStatus{
    boolean isNewTransaction()// 是否是新的事物
    boolean hasSavepoint()// 是否有恢復點
    void setRollbackOnly();  // 設定為只回滾
    boolean isRollbackOnly()// 是否為只回滾
    boolean isCompleted; // 是否已完成
}

由於篇幅有限,我將在下一篇文章透過轉賬實體介紹Spring程式設計式和宣告式事務。

666. 彩蛋




如果你對 Dubbo 感興趣,歡迎加入我的知識星球一起交流。

知識星球

目前在知識星球(https://t.zsxq.com/2VbiaEu)更新瞭如下 Dubbo 原始碼解析如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 伺服器

16. P2P 伺服器

17. HTTP 伺服器

18. 序列化 Serialization

19. 叢集容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing


一共 60 篇++

贊(0)

分享創造快樂