面試官:你知道哪幾種事務失效的場景?

前言

  • 聲明式事務是Spring功能中最爽之一,可是有些時候,我們在使用聲明式事務並未生效,這是爲什麼呢?

  • 今天陳某帶大家來聊一聊聲明事務的幾種失效場景。本文將會從以下兩個方面來說一下事務爲什麼會失效?

  1. @Transactional介紹

  2. @Transactional失效場景

@Transactional介紹

  • @Transactional是聲明式事務的註解,可以被標記在類上接口方法上。

  • 該註解中有很多值得深入瞭解的幾種屬性,我們來看一下。

transactionManager

  • 指定事務管理器,值爲bean的名稱,這個主要用於多事務管理器情況下指定。比如多數據源配置的情況下。

isolation

  • 事務的隔離級別,默認是Isolation.DEFAULT

  • 幾種值的含義如下:

    • Isolation.DEFAULT:事務默認的隔離級別,使用數據庫默認的隔離級別。

    • Isolation.READ_UNCOMMITTED:這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的數據。這種隔離級別會產生髒讀,不可重複讀和幻讀。

    • Isolation.READ_COMMITTED:保證一個事務修改的數據提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。這種事務隔離級別可以避免髒讀出現,但是可能會出現不可重複讀和幻讀。

    • Isolation.REPEATABLE_READ:這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻讀。

    • Isolation.SERIALIZABLE:這是花費最高代價但是最可靠的事務隔離級別。事務被處理爲順序執行。除了防止髒讀,不可重複讀外,還避免了幻讀。

propagation

  • 代表事務的傳播行爲,默認值爲Propagation.REQUIRED

  • Propagation.REQUIRED:如果存在一個事務,則支持當前事務。如果沒有事務則開啓一個新的事務。比如A方法內部調用了B方法,此時B方法將會使用A方法的事務。

  • Propagation.MANDATORY:支持當前事務,如果當前沒有事務,就拋出異常。

  • Propagation.NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。

  • Propagation.NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

  • Propagation.REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。比如A方法使用默認的事務傳播屬性,B方法使用REQUIRES_NEW,此時A方法在內部調用B方法,一旦A方法出現異常,A方法中的事務回滾了,但是B方法並沒有回滾,因爲A和B方法使用的不是同一個事務,B方法新建了一個事務。

  • Propagation.NESTED:支持當前事務,新增Savepoint點,也就是在進入子事務之前,父事務建立一個回滾點,與當前事務同步提交或回滾。子事務是父事務的一部分,在父事務還未提交時,子事務一定沒有提交。嵌套事務一個非常重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所做的動作。而內層事務操作失敗並不會引起外層事務的回滾。

timeout

  • 事務的超時時間,單位爲秒。

readOnly

  • 該屬性用於設置當前事務是否爲只讀事務,設置爲true表示只讀,false則表示可讀寫,默認值爲false。如果一個事務只涉及到只讀,可以設置爲true。

rollbackFor 屬性

  • 用於指定能夠觸發事務回滾的異常類型,可以指定多個異常類型。

  • 默認是在RuntimeExceptionError上回滾。

noRollbackFor

  • 拋出指定的異常類型,不回滾事務,也可以指定多個異常類型。

@Transactional失效場景

  • 聲明式事務失效的場景有很多,陳某這裏只是羅列一下幾種常見的場景。

底層數據庫引擎不支持事務

  • 如果數據庫引擎不支持事務,則Spring自然無法支持事務。

在非public修飾的方法使用

  • @Transactional註解使用的是AOP,在使用動態代理的時候只能針對public方法進行代理,源碼依據在AbstractFallbackTransactionAttributeSource類中的computeTransactionAttribute方法中,如下:

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
}
  • 此處如果不是標註在public修飾的方法上並不會拋出異常,但是會導致事務失效。

異常被 " 踹死了 "

  • 這種情況小白是最容易犯錯的,在整個事務的方法中使用try-catch,導致異常無法拋出,自然會導致事務失效。僞代碼如下:

@Transactional
public void method(){
  try{
    //插入一條數據
    //更改一條數據
  }catch(Exception ex){
    return;
  }
}

方法中調用同類的方法

  • 簡單的說就是一個類中的A方法(未標註聲明式事務)在內部調用了B方法(標註了聲明式事務),這樣會導致B方法中的事務失效。

  • 代碼如下:

public class Test{
  public void A(){
    //插入一條數據
    //調用B方法
    B();
  }
  
  @Transactional
  public void B(){
    //插入數據
  }
}
  • 爲什麼會失效呢?:其實原因很簡單,Spring在掃描Bean的時候會自動爲標註了@Transactional註解的類生成一個代理類(proxy),當有註解的方法被調用的時候,實際上是代理類調用的,代理類在調用之前會開啓事務,執行事務的操作,但是同類中的方法互相調用,相當於this.B(),此時的B方法並非是代理類調用,而是直接通過原有的Bean直接調用,所以註解會失效。

  • 如何解決呢?:這就涉及到註解失效的原因了,後續文章會介紹到,這裏不過多介紹了。

rollbackFor屬性設置錯誤

  • 很容易理解,指定異常觸發回滾,一旦設置錯誤,導致一些異常不能觸發回滾,此時的聲明式事務不就失效了嗎。

noRollbackFor屬性設置錯誤

  • 這個和rollbackFor屬性設置錯誤類似,一旦設置錯誤,也會導致異常不能觸發回滾,此時的聲明式事務會失效。

propagation屬性設置錯誤

  • 事務的傳播屬性在上面已經介紹了,默認的事務傳播屬性是Propagation.REQUIRED,但是一旦配置了錯誤的傳播屬性,也是會導致事務失效,如下三種配置將會導致事務失效:

    • Propagation.SUPPORTS

    • Propagation.NOT_SUPPORTED

    • Propagation.NEVER

原始SSM項目,重複掃描導致事務失效

  • 在原始的SSM項目中都配置了context:component-scan並且同時掃描了service層,此時事務將會失效。

  • 按照Spring配置文件的加載順序來說,會先加載Springmvc的配置文件,如果在加載Springmvc配置文件的時候把service也加載了,但是此時事務還沒加載,將會導致事務無法成功生效。

  • 解決方法很簡單,把掃描service層的配置設置在Spring配置文件或者其他配置文件中即可。

總結

  • 事務失效的原因很多,但是千萬不要做到一知半解,只有深入理解了,才能在面試過程中對答如流。

  • 今天的文章就到此結束了,如果覺得陳某寫得不錯,有所收穫的,關注在看來一波,你們的鼓勵,將會是我寫作的動力,謝謝支持!!!

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