Spring 之 @Transaction 詳解

介紹

@Transaction 是 Spring 提供用來控制事務回滾/提交的一個註解,讓我們從編程式註解轉換到聲明式註解。在這裏就不做過多的撰述,今天主要來看下 @Transaction 裏面的屬性使用。

作用域

@Transaction 可以寫在類、接口、方法上

  • 當標註在類上的時候:表示給該類所有的 public 方法添加上 @Transaction 註解
  • 當標註在接口上的時候:Spring 建議不要在接口或者接口方法上使用該註解,因爲這只有在使用基於接口的代理時它纔會生效。像 CGLib 動態代理採用繼承的方式將會導致 @Transactional 註解失效
  • 當標註在方法上的時候:事務的作用域就只在該方法上生效,並且如果類及方法上都配置 @Transaction 註解時,方法的註解會覆蓋類上的註解

屬性

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

可以看到 @Transaction 相關所有的屬性值

字段名 類型 含義
value String 主要用來指定不同的事務管理器
滿足在同一個系統中,存在不同的事務管理器
propagation enum: Propagation 可選的事務傳播行爲設置
isolation enum: Isolation 可選的事務隔離級別設置
readOnly boolean 讀寫或只讀事務,默認讀寫
timeout int (in seconds granularity) 事務超時時間設置
rollbackFor Class對象數組,必須繼承自Throwable 導致事務回滾的異常類數組
rollbackForClassName 類名數組,必須繼承自Throwable 導致事務回滾的異常類名字數組
noRollbackFor Class對象數組,必須繼承自Throwable 不會導致事務回滾的異常類數組
noRollbackForClassName 類名數組,必須繼承自Throwable 不會導致事務回滾的異常類名字數組

value

value 主要用來指定不同的事務管理器,滿足在同一個系統中,存在不同的事務管理器。如果在 Spring 中,配置了多個數據源聲明瞭多個事務管理器,可以通過該參數來進行指定事務管理器。

propagation

事務傳播行爲有一下7種,默認是 REQUIRED 傳播機制。

含義
REQUIRED

如果當前存在事務,則加入該事務;

如果當前不存在事務,則創建一個新的事務;

SUPPORTS

如果當前存在事務,則加入該事務;

如果當前不存在事務,則以非事務的方式繼續運行;

MANDATORY

如果當前存在事務,則加入該事務;

如果當前不存在事務,則拋出異常;

REQUIRES_NEW

如果當前不存在事務,重新創建一個新的事務;

如果當前存在事務,則暫停當前的事務;

NOT_SUPPORTED

以非事務的方式運行

如果當前存在事務,則暫停當前的事務

NEVER

以非事務的方式運行

如果當前存在事務,則拋出異常

NESTED

如果當前存在事務,則在該事務內嵌套事務運行;

如果當前不存在事務,則創建一個新的事務;

這裏比較容易引起疑問的是 REQUIRED 和 NESTED 有什麼差別?也是面試的重災區。

@Service
public class ServiceA{
    @Autowired
    private ServiceB serviceB;
    
    @Transaction
    public void A(){
        try{
            serviceB.B();
        }catch(Exception e){
            e.printStackTrace();
        } 
        //僞代碼,執行數據庫修改操作
    }
}
@Service
public class ServiceB{
    
    @Transaction(propagation = Propagation.REQUIRED)
    public void B(){
        //僞代碼,執行數據庫修改操作
    }
}

如上存在 ServiceA、ServiceB 兩個類和A、B兩個方法(這裏指的異常都是 RuntimeException 異常或其子類)

  • 情況一:A方法中出現了異常,結果A、B方法修改操作都會被回滾
  • 情況二:B方法中出現了異常,結果A、B方法修改操作都會被回滾
@Service
public class ServiceB{
    
    @Transaction(propagation = Propagation.NESTED)
    public void B(){
        //僞代碼,執行數據庫修改操作
    }
}

接下來將B方法的 propagation 修改爲 NESTED 事務傳播機制

  • 情況一:A方法中出現了異常,結果A、B方法修改操作都會被回滾
  • 情況二:B方法中出現了異常,結果B方法修改操作被回滾,A方法修改操作提交

通過如上案例,希望可以幫助大家掌握這兩個事務傳播機制的差異。

isolation

isolation 對應的事務隔離級別與 MySQL 一致,有不熟悉的同學可以另外瞭解一下。主要用來避免髒讀、不可重複讀以及幻讀的問題。

 

readOnly

設置爲 true:表示只讀,如果該方法內存在增、刪、改操作則會拋出異常;

設置爲false(默認):表示讀寫,增、刪、改、查操作都允許;

rollbackFor 和 rollbackForClassName

這兩個屬性都是用來指定回滾的異常類型

  • rollbackFor:通過類進行指定,如@Transactional(rollbackFor = {Exception.class})
  • rollbackForClassName:通過類名進行指定,如@Transactional(rollbackForClassName = {"java.lang.Exception"})

可能有同學會有疑問,爲什麼需要知道回滾的異常類型呢?不是默認有異常就回滾嘛?

Spring默認拋出了未檢查unchecked異常(繼承自 RuntimeException 的異常)或者 Error纔回滾事務;其他異常不會觸發回滾事務。如果在事務中拋出其他類型的異常,但卻期望 Spring 能夠回滾事務,就需要指定 rollbackFor 或者 rollbackForClassName 屬性。

noRollbackFor 和 noRollbackForClassName

與 rollbackFor 和 rollbackForClassName 相反,是用來指定不回滾的異常類型,使用方法一致。

手動回滾

spring 也提供了 @Transaction 對應的手動回滾方式

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

如果在代碼中一定要 catch 住異常記得使用手動回滾的方式或者重新拋出一個異常。

失效場景

接下來看一下場景的 @Transaction 註解失效的場景,希望大家以後能夠避免踩坑:

  1. @Transactional 應用在非 public 修飾的方法上
  2. @Transactional 註解屬性 propagation 設置問題
  3. @Transactional 註解屬性 rollbackFor、noRollbackFor 設置問題
  4. 同一個類中方法調用,導致 @Transactional 失效
  5. 異常被 catch 導致 @Transactional 失效

總結

本文重點闡述了 @Transaction 註解的屬性使用,幫助大家快速瞭解。希望能夠有所幫助!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章