- @Transactional失敗的情況
- @Transactional的源碼分析
- @Transactional失敗的情況以及對應的源碼分析
文章目錄
- 1.@Transactional 失敗的情況
- 1.1 類内部通路
- 1.2 私有方法
- 1.3 異常不比對
- 1.4 多線程
- 2.@Transactional 源碼分析
- 2.1 @Transactional 執行機制
- 3.@Transactional 失效原因
- 3.1 private 導緻事務不生效原因
- 3.2 異常不比對原因
1.@Transactional 失敗的情況
連結: javascript:void(0)
UserDao
public interface UserDao {
// select * from user_test where uid = "#{uid}"
public MyUser selectUserById(Integer uid);
// update user_test set uname =#{uname},usex = #{usex} where uid = #{uid}
public int updateUser(MyUser user);
}
UserController
@Service
public class UserController {
@Autowired
private UserDao userDao;
public void update(Integer id) {
MyUser user = new MyUser();
user.setUid(id);
user.setUname("張三-testing");
user.setUsex("女");
userDao.updateUser(user);
}
public MyUser query(Integer id) {
MyUser user = userDao.selectUserById(id);
return user;
}
// 正常情況
@Transactional(rollbackFor = Exception.class)
public void testSuccess() throws Exception {
Integer id = 1;
MyUser user = query(id);
System.out.println("原記錄:" + user);
update(id);
throw new Exception("事務生效");
}
}
@Transactional 事務不生效的幾種情況
- 類内部通路:A 類的 a1 方法沒有标注 @Transactional,a2 方法标注 @Transactional,在 a1 裡面調用 a2;
- 私有方法:将 @Transactional 注解标注在非 public 方法上;
- 異常不比對:@Transactional 未設定 rollbackFor 屬性,方法傳回 Exception 等異常;
- 多線程:主線程和子線程的調用,線程抛出異常。
1.1 類内部通路
UserController.testInteralCall()
public void testInteralCall() throws Exception {
testSuccess();
throw new Exception("事務不生效:類内部通路");
}
這裡 testInteralCall() 沒有标注 @Transactional
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserController uc = (UserController) applicationContext.getBean("userController");
try {
uc.testSuccess();
} finally {
MyUser user = uc.query(1);
System.out.println("修改後的記錄:" + user);
}
}
// 原記錄:MyUser(uid=1, uname=張三, usex=女)
// 修改後的記錄:MyUser(uid=1, uname=張三-testing, usex=女)
@Transactional 的工作機制是基于 AOP 實作, Aop 是動态代理, 通過代理調用testSuccess(), 通過Aop增強, 增強的邏輯其實就是在 testSuccess() 的前後分别加上開啟、送出事務的邏輯。
通過 testInteralCall() 去調用 testSuccess(), testSuccess() 前後不會進行任何增強操作,
類内部調用, 不會進行代理方式通路
擷取本對象的代理對象,再進行調用。
@Service
public class OrderService {
public void insert() {
OrderService proxy = (OrderService) AopContext.currentProxy();
proxy.insertOrder();
}
@Transactional
public void insertOrder() {
//SQL操作
}
}
1.2 私有方法
在私有方法上,添加 @Transactional 注解也不會生效
@Transactional(rollbackFor = Exception.class)
private void testPirvateMethod() throws Exception {
Integer id = 1;
MyUser user = query(id);
System.out.println("原記錄:" + user);
update(id);
throw new Exception("測試事務生效");
}
1.3 異常不比對
@Transactional 沒有設定 rollbackFor = Exception.class
@Transactional
public void testExceptionNotMatch() throws Exception {
Integer id = 1;
MyUser user = query(id);
System.out.println("原記錄:" + user);
update(id);
throw new Exception("事務不生效:異常不比對");
}
@Transactional 注解預設處理運作時異常, RuntimeException.class
1.4 多線程
父線程抛出異常
父線程抛出異常,子線程不抛出異常
public void testSuccess() throws Exception {
Integer id = 1;
MyUser user = query(id);
System.out.println("原記錄:" + user);
update(id);
}
@Transactional(rollbackFor = Exception.class)
public void testMultThread() throws Exception {
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
testSuccess();
}
}).start();
throw new Exception("測試事務不生效");
}
父線程抛出線程, 事務復原, 因為子線程是單獨存在的, 和父線程不在同一個事務, 是以子線程的修改不會復原。
子線程抛出異常
父線程不抛出異常,子線程抛出異常
ublic void testSuccess() throws Exception {
Integer id = 1;
MyUser user = query(id);
System.out.println("原記錄:" + user);
update(id);
throw new Exception("測試事務不生效");
}
@Transactional(rollbackFor = Exception.class)
public void testMultThread() throws Exception {
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
testSuccess();
}
}).start();
}
由于子線程的異常不會被外部的線程捕獲,是以父線程不抛異常,事務復原沒有生效。
2.@Transactional 源碼分析
2.1 @Transactional 執行機制
interceptorOrInterceptionAdvice 是 TransactionInterceptor的執行個體
this 是 ReflectiveMethodInvocation 對象,成員對象包含 UserController 類、testSuccess() 方法、入參和代理對象等。
invoke():
事務的核心邏輯: 事務是否開啟、目标方法執行、事務復原、事務送出。
3.@Transactional 失效原因
3.1 private 導緻事務不生效原因
getTransactionAttribute(): 擷取txAttr 變量, 讀取@Transactional 的配置, 如果這個txAttr = null, 不會走後面邏輯
getTransactionAttribute()
allowPublicMethodsOnly() 一直傳回 false, isPublic() 判斷是否是 Public
3.2 異常不比對原因
rollbackOn(): 判斷該異常是否能進行復原, 這個需要判斷主方法抛出的 Exception() 異常,是否在 @Transactional 的配置中。
getDepth(): 異正常則比對邏輯
如果沒有為 null, 那麼會走預設的異常捕獲方式。