Spring 事務注解@Transactional


事務管理一般有編程式和聲明式兩種,編程式是直接在代碼中進行編寫事物處理過程,而聲名式則是通過注解方式或者是在xml文件中進行配置,相對編程式很方便。

而注解方式通過@Transactional 是常見的。我們可以使用@EnableTransactionManagement 注解來啟用事務管理功能,該注解可以加在啟動類上或者單獨加個配置類來處理。

1、Transactional 注解的屬性

  • name 當在配置文件中有多個 TransactionManager , 可以用該屬性指定選擇哪個事務管理器。
  • propagation 事務的傳播行為,默認值為 REQUIRED。
  • isolation 事務的隔離度,默認值采用 DEFAULT。
  • timeout 事務的超時時間,默認值為-1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。
  • read-only 指定事務是否為只讀事務,默認值為 false;為了忽略那些不需要事務的方法,比如讀取數據,可以設置 read-only 為 true。
  • rollback-for 用於指定能夠觸發事務回滾的異常類型,如果有多個異常類型需要指定,各類型之間可以通過逗號分隔。
  • no-rollback- for 拋出 no-rollback-for 指定的異常類型,不回滾事務。

  propagation 屬性(事務傳播性)

  •   REQUIRED 支持當前已經存在的事務,如果還沒有事務,就創建一個新事務。
  •   MANDATORY 支持當前已經存在的事務,如果還沒有事務,就拋出一個異常。
  •   NESTED 在當前事務中創建一個嵌套事務,如果還沒有事務,那么就簡單地創建一個新事務。
  •   REQUIRES_NEW 掛起當前事務,創建一個新事務,如果還沒有事務,就簡單地創建一個新事務。
  •   NEVER 強制要求不在事務中運行,如果當前存在一個事務,則拋出異常。
  •   NOT_SUPPORTED 強制不在事務中運行,如果當前存在一個事務,則掛起該事務。
  •   SUPPORTS 支持當前事務,如果沒有事務那么就不在事務中運行。

2、Transactional應用

@Transactional 可以加在方法上,表示對當前方法配置事務也可以添加到類級別上。

也可以添加到類級別上。當把@Transactional 注解放在類級別時,表示所有該類的公共方法都配置相同的事務屬性信息。

當類級別配置了@Transactional,方法級別也配置了@Transactional,應用程序會以方法級別的事務屬性信息來管理事務,即方法級別的事務屬性信息會覆蓋類級別的相關配置信息。

3、Transactional工作原理

聲明式事務管理包含三個組成部分:

  • 事務的切面

  • 事務管理器

  • EntityManager Proxy本身

事務的切面

事務的切面是一個“around(環繞)”切面,在注解的業務方法前后都可以被調用。實現切面的具體類是TransactionInterceptor。事務的切面有兩個主要職責:

  • 在’before’時,切面提供一個調用點,來決定被調用業務方法應該在正在進行事務的范圍內運行,還是開始一個新的獨立事務。

  • 在’after’時,切面需要確定事務被提交,回滾或者繼續運行。

在’before’時,事務切面自身不包含任何決策邏輯,是否開始新事務的決策委派給事務管理器完成。

事務管理器

事務管理器需要解決下面兩個問題:

  • 新的Entity Manager是否應該被創建?

  • 是否應該開始新的事務?

這些需要事務切面’before’邏輯被調用時決定。事務管理器的決策基於以下兩點:

  • 事務是否正在進行

  • 事務方法的propagation屬性(比如REQUIRES_NEW總要開始新事務)

如果事務管理器確定要創建新事務,那么將:

  • 創建一個新的entity manager

  • entity manager綁定到當前線程

  • 從數據庫連接池中獲取連接

  • 將連接綁定到當前線程

使用ThreadLocal變量將entity manager和數據庫連接都綁定到當前線程。事務運行時他們存儲在線程中,當它們不再被使用時,事務管理器決定是否將他們清除。程序的任何部分如果需要當前的entity manager和數據庫連接都可以從線程中獲取。

EntityManager proxy

當業務方法調用類似entityManager.persist()方法時,這不是由entity manager直接調用的,而是業務方法調用代理,因為事物管理器將entity manage綁定到了線程上,代理從線程獲取當前的entity manager。

4、附注

  4.1 @Transactional 注解應用到 public 方法,才能進行事務管理。因為aop會進行攔截是否是public方法:

//AbstractFallbackTransactionAttributeSource類
  
    protected TransactionAttribute computeTransactionAttribute(Method method,
        Class<?> targetClass) {
            // Don't allow no-public methods as required.
            if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
       }
  }

  4.2 propagation 屬性

  下面三種 propagation 可以不啟動事務。錯誤的配置這三種 propagation,事務可能不會發生回滾。

  • TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。

  4.3 rollbackFor 屬性
  默認情況下,如果在事務中拋出了未檢查異常(繼承自 RuntimeException 的異常)或者 Error,則 Spring 將回滾事務;除此之外,Spring 不會回滾事務。
  可以通過rollbackFor來制定在事物中拋出的其他類型的異常來支持事務回滾,例:

@Transactional(propagation= Propagation.REQUIRED, rollbackFor= MyException.class)

  若在目標方法中拋出的異常是 rollbackFor 指定的異常的子類,事務同樣會回滾。

  4.4 在默認的代理模式下,只有目標方法由外部調用,才能被 Spring 的事務攔截器攔截。在同一個類中的兩個方法直接調用,是不會被 Spring 的事務攔截器攔截

  像如下這種在同一個類中的兩個方法上加上事務控制,其中method上的事務是不能生效的,一種方法就是把它寫到另一個列中,然后再當前類中調用

@Transactional(propagation = Propagation.REQUIRED)
@Override
public void save() {

    method();

    。。。業務處理

    if (true) {
        throw new RuntimeException("save 拋異常了");
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method() {
    。。。業務處理
}

 

 

今日話:

  生活或許就是這樣吧,忙的時候你想着閑下來,閑下來太久了又想着忙些什么。

源碼參照:Github


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM