無需看源碼瞭解並解決一個事務常見的異常

無需看源碼瞭解並解決一個事務常見的異常

在觀看此篇文章以前須要瞭解什麼是事務的傳播屬性git

在觀看此篇文章以前須要瞭解什麼是事務的傳播屬性github

在觀看此篇文章以前須要瞭解什麼是事務的傳播屬性post

Transaction rolled back because it has been marked as rollback-only3d

相信你們在使用Spring事務的時候有機率會碰到一個異常,這個異常就是UnexpectedRollbackException異常的描述就是上面所寫的。至於這個異常是怎麼報出來的呢?咱們先模擬出來,而後進行着手分析。咱們模擬的場景是轉錢的場景,即A給B轉100塊錢。code

@Service
public class UserAccountA {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private UserAccountB userAccountB;

    @Transactional
    public void eventTwo() throws RollbackException {
        jdbcTemplate.execute("UPDATE USER SET MONEY = MONEY - 100 WHERE NAME = 'A'");
        userAccountB.eventTwo();
    }

}
@Service
public class UserAccountB {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = RollbackException.class)
    public void eventTwo() throws RollbackException {
        jdbcTemplate.execute("UPDATE USER SET MONEY = MONEY + 100 WHERE NAME = 'B'");
        throw new RollbackException();
    }

}
public class RollbackException extends Exception {
}

上面的三個類分別是用戶A、用戶B、和自定義的異常。這時候咱們運行程序的時候就能看到那個異常了。那麼爲何會報此異常呢。這裏咱們有兩個須要注意的點。blog

  • 用戶B定義的傳播屬性:Propagation.REQUIRED 此傳播屬性下的意思就是支持當前事務,若是當前沒有事務那麼就新建事務,若是有事務就加入此事務。因此此時B方法加入到了A方法建立的事務中
  • 自定義異常類:咱們自定義的異常類是繼承了Exception。下面的是官方文檔的一句話,意思就是默認狀況下當拋出RuntimeException纔會回滾。

By default, a transaction will be rolling back RuntimeException and Error繼承

有了上面兩個知識的補充,下面咱們開始講爲何會報這個異常,咱們都知道Spring的事務實際上是用了動態搭理的原理實現的。事務

大概嵌套邏輯就像上圖同樣,可是在B方法執行完成之後拋出了異常,須要回滾,可是並不會真正的回滾,而是將此事務標記爲rollback-only,當A方法執行完之後,咱們能夠看到在A方法上面並無rollbackFor這個參數設置,而拋出的異常也是繼承了Exception因此A方法執行完之後想要提交事務,因此此時就會產生衝突。文檔

同一個事務中,一個方法想要回滾,一個方法想要提交。固然會拋異常了。get

解決辦法

解決辦法很簡單,首先咱們看是什麼致使了這個問題。同一個事務中,一個方法想要回滾,一個方法想要提交。。致使這個問題緣由有兩個。

  1. 同一個事務
  2. 一個想要回滾,一個想要提交

因此針對上面緣由咱們能夠有兩種不一樣的解決方法。

  1. 將B方法的傳播屬性改成PROPAGATION_REQUIRES_NEW,即不會和A共用一個事務,會本身新啓動一個事務。此時A和B兩個方法互不干擾。
  2. 在A方法上的事務註解上加上rollbackFor = RollbackException.class參數,此時A方法收到B方法拋出的異常後,也會回滾了。

本文代碼地址

相關文章
相關標籤/搜索