說明
首先我們都瞭解事務爲什麼回滾,回滾的原因是什麼。默認情況下,事務只有遇到運行期異常時纔會回滾,而在遇到檢查型異常時不會回滾。
問題描述
我們定義兩個類,一個類中有兩個事務方法,如圖:
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