Spring事務-代理模式導致事務不回滾問題分析

說明

  首先我們都瞭解事務爲什麼回滾,回滾的原因是什麼。默認情況下,事務只有遇到運行期異常時纔會回滾,而在遇到檢查型異常時不會回滾。

問題描述

我們定義兩個類,一個類中有兩個事務方法,如圖:

package com.helu.samui.service;

import com.helu.samui.dao.UserInfoDao;
import com.helu.samui.entity.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "userInfoService")
public class UserInfoService {

    @Autowired
    private UserInfoDao userInfoDao;
    @Autowired
    private TestService testService;

    @Transactional
    public void save() {
        userInfoDao.insert(getUserInfo("a1"));
        try {

            //內部類調用不會觸發代理模式
            this.test();
            //testService.test();
        } catch (Exception e) {
            System.out.println("aaaaaaaaaaaaaaa");
            e.printStackTrace();
        }
        userInfoDao.insert(getUserInfo("a2"));
    }

    @Transactional
    public void test() {
        throw new RuntimeException();
    }

    private UserInfo getUserInfo(String loginName) {
        UserInfo userInfo = new UserInfo();
        userInfo.setLoginName(loginName);
        return userInfo;
    }
}

如圖: save()方法上加了@Transactional註解,表示開啓了一個事務,在其中進行了兩次插入操作,中間調用this.test()這個同一個類中方法,我們可以發現在代碼執行到save()的時候,異常被捕獲,事務未被回滾。
輸出:

開始測試-----------------
catch the exception.
測試結束-----------------

數據庫: 插入兩條數據
在這裏插入圖片描述
注: 這種情況符合我們異常拋出回滾,異常被捕獲,不回滾的邏輯判斷。但是別急,我們看下面這種情況。我們將UserInfoService 中的save()方法中兩次插入中間的調用改成testService.test(),去調用testService的test()方法。

package com.helu.samui.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "testService")
public class TestService {

    @Transactional
    public void test() {
        throw new RuntimeException();
    }
}

我們再來看下輸出和數據庫的情況:
輸出:
在這裏插入圖片描述
數據庫: 沒有任何數據插入
  所以,這是爲什麼呢?調用類自己內部的事務方法的時候,被調用的事務方法拋出異常,異常被捕獲了,不會回滾;調用其他類中的事務方法,被調用的事務方法拋出異常,異常也被捕獲了,我們看到異常打印出了catch the exception,但是事務回滾了,這是什麼原因呢?

分析

  我們從日誌着手,首先,同樣異常都被捕獲住了,但是調用其他類中的事務方法的時候多拋出了一個異常,UnexpectedRollbackException,並且提示事務被回滾,因爲rollback-only已經被標記成需要回滾了,那我們就根據異常一步一步分析吧。
  我們已經知道回滾標記已經被置成true,異常是testService中拋出,我們看看爲什麼回滾標記,什麼時候被置爲true的。首先,事務的實現是基於代理模式實現,當調用testService中的test方法的時候觸發了代理模式,而直接調用this.test(),因爲this是被真實對象本身,所以並不會觸發代理模式,這就是這兩者區別的真正原因。

源碼分析可以參照:http://www.cnblogs.com/chihirotan/p/6739748.html

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