Spring的@Transactional 嵌套研究

事務嵌套局部回滾的問題,很是費解。

本文將做一個詳細的測試,加強對Spring的@Transactional 理解和使用

1、兩個單獨不干擾事務

	@RequestMapping("/test")
    public void test() {
        LoveFile test1 = new LoveFile();
        test1.setFileUuid(get32Uuid());
        test1.setFileName("test1");
        loveFileService.test1(test1);
        LoveFile test2 = new LoveFile();
        test2.setFileUuid(null);
        test2.setFileName("test2");
        loveFileService.test2(test2);
    }
    @Override
    @Transactional
    public void test1(LoveFile loveFile) {
        mapper.insertSelective(loveFile);

    }
    @Override
    @Transactional
    public void test2(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }

由於我給第二個test2插入主鍵爲空,報錯。因此庫裏只有一條

總結:兩個單獨事務互不干擾。錯了就是錯了,很好理解

2、普通嵌套事務

	@RequestMapping("/test")
    @Transactional
    public void test() {
        LoveFile test1 = new LoveFile();
        test1.setFileUuid(get32Uuid());
        test1.setFileName("test1");
        loveFileService.test1(test1);
        LoveFile test2 = new LoveFile();
        test2.setFileUuid(null);
        test2.setFileName("test2");
        loveFileService.test2(test2);
    }

子事務不變,刪除剛纔的數據後,測試。

庫裏空空。

總結:加在外層的事務起了作用,在test2報錯時回滾了test1

3、嵌套事務、三個子事務,中間一個加(propagation = Propagation.REQUIRES_NEW)

Propagation取值:

REQUIRED(默認值):在有transaction狀態下執行;如當前沒有transaction,則創建新的transaction;

SUPPORTS:如當前有transaction,則在transaction狀態下執行;如果當前沒有transaction,在無transaction狀態下執行;

MANDATORY:必須在有transaction狀態下執行,如果當前沒有transaction,則拋出異常IllegalTransactionStateException;

REQUIRES_NEW:創建新的transaction並執行;如果當前已有transaction,則將當前transaction掛起;

NOT_SUPPORTED:在無transaction狀態下執行;如果當前已有transaction,則將當前transaction掛起;

NEVER:在無transaction狀態下執行;如果當前已有transaction,則拋出異常IllegalTransactionStateException。

本文重點研究 REQUIRES_NEW 

@RequestMapping("/test")
    @Transactional
    public void test() {
        LoveFile test1 = new LoveFile();
        test1.setFileUuid(get32Uuid());
        test1.setFileName("test1");
        loveFileService.test1(test1);
        LoveFile test3 = new LoveFile();
        test3.setFileUuid(get32Uuid());
        test3.setFileName("test3");
        loveFileService.test3(test3);
        LoveFile test2 = new LoveFile();
        test2.setFileUuid(null);
        test2.setFileName("test2");
        loveFileService.test2(test2);
    }
@Override
    @Transactional
    public void test1(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }
    @Override
    @Transactional
    public void test2(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void test3(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }
REQUIRES_NEW官方文檔解釋:
Create a new transaction, and suspend the current transaction if one exists.
意思是,創建一個新事務,如果當前存在事務,將這個事務掛起。

也就是說test3,是獨立於外層事務的單獨的事務,他已經掛起的外層事務,他要把自己的問題處理完再去管別人。

因此結果是

總結:不給子事務加(propagation = Propagation.REQUIRES_NEW),相當於加入了外層事務。加了則當做新的子事務。

test2報錯了,影響了test1 ,卻不能影響test3,已經說明了此問題

 

4、嵌套事務、三個子事務,中間一個加(propagation = Propagation.REQUIRES_NEW),偏偏出錯了

	@RequestMapping("/test")
    @Transactional
    public void test() {
        LoveFile test1 = new LoveFile();
        test1.setFileUuid(get32Uuid());
        test1.setFileName("test1");
        loveFileService.test1(test1);
        LoveFile test3 = new LoveFile();
        test3.setFileUuid(null);
        test3.setFileName("test3");
        loveFileService.test3(test3);
        LoveFile test2 = new LoveFile();
        test2.setFileUuid(get32Uuid());
        test2.setFileName("test2");
        loveFileService.test2(test2);
    }

我把test3主鍵爲空,因此他會有SQLException。來研究下,他會不會影響整體。

總結: 雖然他是開闢的新事物,但是出錯了,還是會牽連整體的

至此,(propagation = Propagation.REQUIRES_NEW) 已經解釋清楚了。

我覺得子事務,不加這個東西,就相當於於廢物。不佳的話直接加入了外層事務。那還要個單單 @Transactional何用?

建議大家對子事務都加上這條件。

 

rollbackFor官方文檔解釋:

在@Transactional註解中如果不配置rollbackFor屬性,那麼事物只會在遇到RuntimeException的時候纔會回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非運行時異常時也回滾

5、rollbackFor了一個異常,卻會報另外異常

@RequestMapping("/test")
    @Transactional
    public void test() {
        LoveFile test1 = new LoveFile();
        test1.setFileUuid(get32Uuid());
        test1.setFileName("test1");
        loveFileService.test1(test1);
        LoveFile test3 = new LoveFile();
        test3.setFileUuid(null);
        test3.setFileName("test3");
        loveFileService.test3(test3);
        LoveFile test2 = new LoveFile();
        test2.setFileUuid(get32Uuid());
        test2.setFileName("test2");
        loveFileService.test2(test2);
    }
 @Override
    @Transactional
    public void test1(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }
    @Override
    @Transactional
    public void test2(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }
    @Override
    @Transactional(rollbackFor=SQLException.class,propagation = Propagation.REQUIRES_NEW)
    public void test3(LoveFile loveFile) {
        int  a = 1/0;
       // mapper.insertSelective(loveFile);
    }

這個test3,一定會報 by zero,他會報錯,我來測試下,他會不會回滾,影響他人。

如此是影響了。

因爲外層事務沒有聲明異常,他看到一個內部報by zero 了,而自己是RuntimeException,包含在內。因此自己回滾了。

test1 是直接加入整體的。沒加出數據,就說明了此問題。

我其實是像測試,test3是否回滾,這個測試案例不合適。

 

@RequestMapping("/test")
    @Transactional
    public void test() {
        LoveFile test1 = new LoveFile();
        test1.setFileUuid(get32Uuid());
        test1.setFileName("test1");
        loveFileService.test1(test1);
        LoveFile test3 = new LoveFile();
        test3.setFileUuid(get32Uuid());
        test3.setFileName("test3");
        loveFileService.test3(test3);
        LoveFile test2 = new LoveFile();
        test2.setFileUuid(get32Uuid());
        test2.setFileName("test2");
        loveFileService.test2(test2);
    }
   @Override
    @Transactional
    public void test1(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }
    @Override
    @Transactional
    public void test2(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
    }
    @Override
    @Transactional(rollbackFor=SQLException.class,propagation = Propagation.REQUIRES_NEW)
    public void test3(LoveFile loveFile) {
        mapper.insertSelective(loveFile);
        int  a = 1/0;
    }

我給了test3 正常的主鍵,讓他添加成功,卻在後面 by zero .因爲我認爲他不會回滾,這次應該是test3加進去了,test1和test2被外層事務回滾。

但是結果是:

我又做了一些測試,發現rollbackFor=SQLException.class 這樣的聲明一個特定的異常沒有任何效果。

rollbackFor 用途不大,就是聲明rollbackFor=Exception.class 時比 RuntimeException 廣一些。

其他時用途不大。

如果想讓特定異常不回滾,還不如用 try catch。只要異常被catch住,不被方法知道,就不會出現error ,也就不會回滾。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章