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

前言

  • 文章首發於面試官:你知道哪些事務失效的場景?
  • 聲明式事務是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配置文件或者其餘配置文件中便可。

總結

  • 事務失效的緣由不少,可是千萬不要作到只知其一;不知其二,只有深刻理解了,才能在面試過程當中對答如流。
  • 今天的文章就到此結束了,若是以爲陳某寫得不錯,有所收穫的,關注在看來一波,歡迎各位朋友關注陳某的公衆號!!!
相關文章
相關標籤/搜索