spring 事務(wù)傳播行為之嵌套事務(wù)NESTED細節(jié)

逖不能興中原而復濟者,有如大江!

經(jīng)過我之前的實踐,可以看出 NESTED事務(wù)申明在調(diào)用者上會新建一個獨立事務(wù)。申明在被調(diào)用者上,若調(diào)用者存在事務(wù)則加入調(diào)用者事務(wù)。調(diào)用者不存在事務(wù)則新建一個獨立事務(wù)。

這個功能好像和spring默認的事務(wù)傳播行為REQUIRED一樣的?
不,它的功能可是比REQUIRED要強大!

我來通過實驗證明NESTED和REQUIRED的區(qū)別

這個例子是基于 http://www.itdecent.cn/p/bc3cbacf9e70 這個文章的代碼

首先,InsertUsers和InsertCuser方法上都申明了REQUIRED,讓他們屬于同一個事務(wù)。將引發(fā)異常的語句 int i = 1/0; 放到 InsertCuser方法里

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void InsertUsers(Users users) {
        jdbcTemplate.update("INSERT INTO users(id,name, age, email) VALUES (?, ?, ?, ?);", users.getId(), users.getName(), users.getAge(), users.getEmail());
        //調(diào)用service中另一個方法
        Cuser cuser = new Cuser(users.getId(), users.getName(), users.getAge(), users.getEmail());
        //打印事務(wù)名
        List<Map<String, Object>> maps = jdbcTemplate.queryForList("SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID( );");
        System.out.println(maps + TransactionSynchronizationManager.getCurrentTransactionName());
        //對InsertCuser拋出的異常進行捕獲處理,并且不再向上拋出
        try {
            cuserService.InsertCuser(cuser);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  @Transactional(propagation=Propagation.REQUIRED)
    @Override
    public void InsertCuser(Cuser cuser) {

        jdbcTemplate.update("INSERT INTO cuser(id,name, age, email) VALUES (?, ?, ?, ?);", cuser.getId(), cuser.getName(), cuser.getAge(), cuser.getEmail());
        //打印事務(wù)名
        List<Map<String, Object>> maps = jdbcTemplate.queryForList("SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID( );");
        System.out.println(maps + TransactionSynchronizationManager.getCurrentTransactionName());
        int i = 1/0;
    }
注意 為什么要加try/catch包裹cuserService.InsertCuser(cuser);語句?

為了杜絕InsertCuser中拋出的異常影響InsertUsers方法的實驗結(jié)果

try {
            cuserService.InsertCuser(cuser);
        } catch (Exception e) {
            e.printStackTrace();
        }

程序運行,結(jié)果是InsertCuser中出現(xiàn)異常,導致事務(wù)回滾、users表和cuser表均無數(shù)據(jù)插入。由于兩個方法被納入同一個事務(wù),因此兩者都會回滾。即使在cuserService.InsertCuser(cuser);上使用try/catch捕獲并不拋出異常也沒用(此方法能保證調(diào)用者方法中的獨立事務(wù)不受被調(diào)用者拋出的異常影響而回滾)

我們再來看,將上面環(huán)境的InsertCuser方法傳播行為改成NESTED

   @Transactional(propagation=Propagation.NESTED)
    @Override
    public void InsertCuser(Cuser cuser) {

        jdbcTemplate.update("INSERT INTO cuser(id,name, age, email) VALUES (?, ?, ?, ?);", cuser.getId(), cuser.getName(), cuser.getAge(), cuser.getEmail());
        //打印事務(wù)名
        List<Map<String, Object>> maps = jdbcTemplate.queryForList("SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID( );");
        System.out.println(maps + TransactionSynchronizationManager.getCurrentTransactionName());
        int i = 1/0;
    }

再次運行,可以看到日志的打印情況。兩方法的事務(wù)的id一致,說明的確是相同事務(wù)


image.png

users表中插入了數(shù)據(jù)說明InsertUsers方法提交成功,cuser表中沒有數(shù)據(jù)說明InsertCuser方法回滾


image.png

那么現(xiàn)在就有一個問題了,既然兩個方法使用同一個事務(wù),為什么沒有一起回滾?

這就是NESTED嵌套事務(wù)的奧秘之處-----它能讓事務(wù)部分回滾

我在網(wǎng)上找到了一句話:

NESTED申明在被調(diào)用方法上,若調(diào)用者方法有開啟事務(wù)。此時NESTED會開始一個 "嵌套的" 事務(wù), 它是已經(jīng)存在事務(wù)的一個真正的子事務(wù)。 潛套事務(wù)開始執(zhí)行時, 它將取得一個 savepoint。 如果這個嵌套事務(wù)失敗, 我們將回滾到此 savepoint。 潛套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會被提交。

這段話中提到的 savepoint 其實是mysql的innodb引擎的特性,為了去了解它我在mysql客戶端對它進行了簡單使用,可以看看這篇文章http://www.itdecent.cn/p/c93c1730e5dc 。 總之它就是一個保存點,生成一個保存點就是生成一個數(shù)據(jù)鏡像。然后無論經(jīng)過了什么sql操作,只要使用回滾至此保存點的命令即可恢復至創(chuàng)建保存點的數(shù)據(jù)狀態(tài)。

那么上面代碼的演示結(jié)果也就說的通了。即使InsertUsers和InsertCuser方法屬于同一個事務(wù),被NESTED嵌套事務(wù)申明的InsertCuser方法出現(xiàn)異常也沒導致REQUIRED申明的InsertUsers的全部回滾,只是部分回滾到了調(diào)用InsertCuser方法之前。因為在調(diào)用InsertCuser方法時會自動生成一個savepoint

InsertUsers方法里出現(xiàn)異常會導致InsertCuser方法嵌套事務(wù)回滾嗎?

將出現(xiàn)異常的代碼行放到這里,結(jié)果都回滾了,畢竟是同一個事務(wù)


image.png
總結(jié)下NESTED的回滾特性
  • 主事務(wù)和嵌套事務(wù)屬于同一個事務(wù)
  • 嵌套事務(wù)出錯回滾不會影響到主事務(wù)
  • 主事務(wù)回滾會將嵌套事務(wù)一起回滾了
進一步證明NESTED嵌套事務(wù)的savepoint機制

可以通過閱讀spring源碼的方式來驗證NESTED是不是使用savepoint機制來實現(xiàn)的,我現(xiàn)在的水平還不足以去閱讀源碼。但是我會很快就有這個能力的,我相信! 不過有簡友已經(jīng)分析過了,可以去看看。寫的很好~
http://www.itdecent.cn/p/2f79ee33c8ad

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容