最近在項目中發現了一則報錯:「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
在個人項目中之因此會報「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相似的操做。 |