基於@Transaction的事務管理

@Transaction中的屬性信息

value :

當在配置文件中配有多個事務管理器時,可用該屬性指定選擇哪一個事務管理器。java

propagation :

事務的傳播行爲。所謂事務的傳播行爲,是指在當前事務開啓以前,已經有一個事務上下文存在了的狀況(好比說咱們給方法A和方法B分別加上了事務,而後在A中調用了B,這時就須要選擇事務傳播行爲,使A作出期待的應對。數據庫

該屬性包含的可選項以下 :併發

  • REQUIRED(默認):
    • 若方法A調用時,上下文中存在事務,則加入當前事務
    • 若方法A調用時,上下文中沒有事務,則新建一個事務
    • 當在方法A調用方法B時,方法A、B將使用同一個事務
    • 若是方法B發生異常須要回滾時,將回滾整個事務
  • REQUIRED_NEW :
    • 對於方法A與B,不管方法調用時是否存在事務,都會開啓一個新的事務
    • 且若是方法調用時存在事務,當前事務將被延緩
    • 若是方法B發生異常,將不會致使方法A的回滾
  • NESTED :
    • 與REQUIRED_NEW相似,但支持JDBC,不支持JPA或Hibernate
  • SUPPORTS :
    • 若是方法調用時,上下文中已經開啓了事務,就使用這個事務
    • 若是方法調用時,上下文中沒有事務,就不使用事務
  • NOT_SUPPORTS :
    • 以非事務的方式運行,若是存在事務,在方法調用到結束階段事務都將被掛起
  • NEVER :
    • 以非事務的方式運行,若是存在事務,則將拋出異常
  • MANDATORY :
    • 強制方法在事務中執行,若是存在事務,則加入當前事務
    • 若是不存在事務,則將拋出異常

isolation :

事務的隔離級別。所謂事務的隔離級別,是指若干個併發的事務之間的隔離程度,由低到高依次分別爲 :read uncommited(讀未提交)、read commited(讀提交)、read repeatable(讀重複)、serializable(序列化);這四個級別能夠逐個解決髒讀、不可重複讀、幻讀這幾類問題。spa

該屬性包含的可選項以下 :代理

  • READ_UNCOMMITED :
    • 最低級別的事務隔離級別,它容許另外一個事務能夠看到這個事務未提交的數據;可致使髒讀,不可重複讀,以及幻讀
  • READ_COMMITED :
    • 保證一個事務提交以後才能被另外一個事務讀取;可防止髒讀,但可能致使不可重複讀和幻讀
  • REPEATABLE_READ :
    • 不只能實現 READ_COMMITED 的功能,還能進行以下限制 :當A事務讀取了一條數據時,B事務將不容許修改這條記錄;阻止髒讀和不可重複讀,但可能致使幻讀
  • SERIALIZABLE :
    • 開銷最大但最可靠的事務隔離級別,事務被處理爲順序執行;除了防止髒讀、不可重複讀以外,還避免了幻讀
  • DEFUALT :
    • 使用當前數據庫的默認隔離級別,如 Oracle、SqlServer 是 READ_COMMITED,MySQL 是 REPEATABLE_READF

其中,髒讀、不可重複讀、幻讀概念以下 :code

  • 髒讀 :讀取了未提交的數據
    • 好比說A操做試圖將帳戶餘額修由2000修改成1000,B操做讀取了該修改後,試圖在原餘額的基礎上增長1000;在B操做執行期間,A操做發生錯誤致使回滾,帳戶餘額又變回了2000;那麼,當B提交操做後,帳戶餘額本該有2000+1000=3000,但因爲B讀取了錯誤的數據,即髒數據,此時的帳戶餘額就僅剩下1000+1000=2000了。
  • 不可重複讀 :先後屢次讀取,數據內容不一致
    • 好比說,咱們有一個操做A須要屢次讀取帳戶餘額,在A的第一次讀取中,餘額爲1000,這時另外一個操做B將餘額修改成了2000,那麼在A的第二次讀取中,就會發現餘額變爲了2000,和第一次讀取的數據不同了;系統在同一個操做中沒法讀取同一個數據,稱爲不可重複讀。
  • 幻讀 :先後屢次讀取,數據總量不一致
    • 好比說事務A在執行讀取操做時,須要屢次統計數據的總量,前一次查詢數據總量後,此時B事務執行了新增數據的操做並提交,這時候A再去讀取,就會像產生幻覺同樣平白多讀出了幾條數據,所以稱爲幻讀。

timeout :

事務的過時時間。默認爲當前數據庫的事務過時時間orm

readonly :

指定當前事務是否爲只讀事務,默認爲false對象

rollbackFor :

指定哪一個或哪些異常發生時,須要引發事務回滾,默認爲Throwable的子類事務

noRollBackFor :

指定哪一個或哪些異常發生時,不引發事務回滾it

Spring中註解方式的事務實現機制

在應用系統調用聲明 @Transactional 的目標方法時,Spring Framework :

  • 默認使用 AOP 代理
  • 在代碼運行時生成一個代理對象

根據 @Transaction 的屬性配置信息,這個代理對象將 :

  • 決定該目標方法是否由攔截器 TransactionInterceptor 來攔截

TransactionInterceptor在攔截時,會 :

  • 首先在目標方法開始執行以前建立並加入事務
  • 而後執行目標方法
  • 最後根據執行狀況是否出現異常,利用抽象事務管理器 AbstarctPlatformTransactionManager 操做數據源 DataSource 提交或回滾事務。

其餘注意事項

@Transaction只有應用到public方法纔能有效

這是由於在使用 Spring AOP 代理時,Spring在調用 TransactionInterceptor 攔截器對方法進行攔截以前,CglibAopProxy 的一個內部類中的 intercept 方法會間接調用 AbstractFallbackTransactionAttributeSource(Spring 經過這個類獲取 @Transaction 註解的事務屬性配置屬性信息)的 computeTransactionAttribute 方法。

這個方法會檢查目標方法的修飾符是否是 public,若是不是,就不會獲取 @Transaction 的屬性配置信息,最終形成不會使用 TransactionInterceptor 來攔截改目標方法,從而致使事務管理不被進行。

避免 Spring 中的 AOP 自調用問題

在 Spring 的 AOP 代理下,只有當目標方法由外部調用的時候,該目標方法才能由 Spring 生成的代理對象來管理。若同一類中,其餘某個沒有 @Transaction 註解的方法調用了有 @Transaction 註解的方法,這個有 @Transaction 註解的方法的事務將被忽略,不會發生回滾。

好比以下這種狀況,咱們有一個Demo類,類中有兩個方法 A 和 B,A 上聲明瞭事務,而 B 上沒有時,當 B 調用了 A,A 中的事務將不會生效。

@Server
public class Demo {
    public void methodB() {
        methodA();
    }
    
    @Transaction
    public void methodA() {
        // ...
    }
}
複製代碼

這是由於,當咱們使用 AOP 進行攔截時,首先會建立一個 Demo 的代理類,這個時候咱們的系統中就會存在兩個 Demo 對象了 :一個是目標 Demo 對象,一個是這個生成的代理 Demo 對象。若是在代理類的 A 方法中調用代理類的 B 方法,這時候 AOP 攔截是能夠生效的,可是,若是在代理類的 A 方法中調用目標類的 B 方法,這時候 AOP 攔截就不會生效了。

相關文章
相關標籤/搜索