UnexpectedRollbackException解決方案

前言

最近在項目中發現了一則報錯:「org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only」。根據報錯信息來看是spring框架中的事務管理報錯:事務回滾了,由於它被標記爲回滾狀態。java

報錯緣由

多層嵌套事務中,若是使用了默認的事務傳播方式,當內層事務拋出異常,外層事務捕捉並正常執行完畢時,就會報出rollback-only異常。
spring框架是使用AOP的方式來管理事務,若是一個被事務管理的方法正常執行完畢,方法結束時spring會將方法中的sql進行提交。若是方法執行過程當中出現異常,則回滾。spring框架的默認事務傳播方式是PROPAGATION_REQUIRED:若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。
在項目中,通常咱們都會使用默認的傳播方式,這樣不管外層事務和內層事務任何一個出現異常,那麼全部的sql都不會執行。在嵌套事務場景中,內層事務的sql和外層事務的sql會在外層事務結束時進行提交或回滾。若是內層事務拋出異常e,在內層事務結束時,spring會把事務標記爲「rollback-only」。這時若是外層事務捕捉了異常e,那麼外層事務方法還會繼續執行代碼,直到外層事務也結束時,spring發現事務已經被標記爲「rollback-only」,但方法卻正常執行完畢了,這時spring就會拋出「org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only」。
代碼示例以下:spring

Class ServiceA {
    @Resource(name = "serviceB")
    private ServiceB b;
    
    @Transactional
    public void a() {
        try {
            b.b()
        } catch (Exception ignore) {
        }
    }
}

Class ServiceB {
    @Transactional
    public void b() {
        throw new RuntimeException();
    }
}

當調用a()時,就會報出「rollback-only」異常。sql

解決方案

  • 若是但願內層事務拋出異常時中斷程序執行,直接在外層事務的catch代碼塊中拋出e.
  • 若是但願程序正常執行完畢,而且但願外層事務結束時所有提交,須要在內層事務中作異常捕獲處理。
  • 若是但願內層事務回滾,但不影響外層事務提交,須要將內層事務的傳播方式指定爲PROPAGATION_NESTED。注:PROPAGATION_NESTED基於數據庫savepoint實現的嵌套事務,外層事務的提交和回滾可以控制嵌內層事務,而內層事務報錯時,能夠返回原始savepoint,外層事務能夠繼續提交。

在個人項目中之因此會報「rollback-only」異常的根本緣由是代碼風格不一致的緣由。外層事務對錯誤的處理方式是返回true或false來告訴上游執行結果,而內層事務是經過拋出異常來告訴上游(這裏指外層事務)執行結果,這種差別就致使了「rollback-only」異常。雖然最後事務依然是回滾了,不影響程序對sql的處理,但外層事務的上游本指望返回true和false,卻收到了UnexpectedRollbackException異常,(╯ ̄Д ̄)╯︵ ┻━┻。數據庫

附:事務傳播方式

@see org.springframework.transaction.annotation.Propagation框架

事務傳播方式 說明
PROPAGATION_REQUIRED 若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。這是默認的傳播方式
PROPAGATION_SUPPORTS 支持當前事務,若是當前沒有事務,就以非事務方式執行
PROPAGATION_MANDATORY 使用當前的事務,若是當前沒有事務,就拋出異常
PROPAGATION_REQUIRES_NEW 新建事務,若是當前存在事務,把當前事務掛起
PROPAGATION_NOT_SUPPORTED 以非事務方式執行操做,若是當前存在事務,就把當前事務掛起
PROPAGATION_NEVER 以非事務方式執行,若是當前存在事務,則拋出異常
PROPAGATION_SUPPORTS 支持當前事務,若是當前沒有事務,就以非事務方式執行。
PROPAGATION_NESTED 若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。
相關文章
相關標籤/搜索