嵌套transaction中的try catch
背景
- 产生异常
1 | org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only |
- 结构
AOP切片在Service方法上(指定Order),切片方法加事务注解,Service方法加事务注解,Service方法中,使用try catch调用Dao方法,Dao方法调用JPA的save方法,save方法存在默认的事务注解,数据库建立联合独立索引。此时第二次保存相同的数据时会异常。
原因
分析此时存在三个方法存在事务注解,即切片方法,Service方法,save方法。而此时三者为同一事务(默认事务传播级别为REQUIRED)。
当save方法异常时,将此事务标记为回滚,并且向上抛出异常,然而上层方法catch异常,使得执行流达到事务方法末尾,使得transaction可以提交,提交时发现已经标记为回滚,抛出此异常。
需要注意的是,此时最多只有两个事务,即切片方法和Service方法是同一个事务。因为其没有通过动态代理实现,因此无法使用子事务,认为其是在同一方法内被调用。
因此,触发的真正条件是,使用JDK方式开启事务,下层事务与上层事务为同一事务,且在上层事务中catch异常。
失败探索
尝试在Service上更改事务传播级别,即改为REQUIRES_NEW,不可行。同理在Service中新建方法标记REQUIRES_NEW也不可行。这是因为事务传播机制导致的。因为Spring事务管理是通过JDK动态代理实现,故直接调用子方法无法触发事务,事务传播失效。事务传播失效
解决方式
- 真正开启新事务。只需要在下层方法使用JDK方式开启时加入开启新事务注解即可。此处即在DAO层加入
@Transactional(propagation = REQUIQRES_NEW)
。 - catch后继续向上传播。可以使用throw直接抛出异常,也可以在上层事务中catch中使用方法继续传播
1
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
参考
rollback-only异常令我对事务有了新的认识
Spring事务嵌套引发的血案—Transaction rolled back because it has been marked as rollback-only
- Title: 嵌套transaction中的try catch
- Author: Ethereal
- Created at: 2023-12-17 22:41:27
- Updated at: 2023-12-17 23:57:18
- Link: https://ethereal-o.github.io/2023/12/17/嵌套transaction中的try-catch/
- License: This work is licensed under CC BY-NC-SA 4.0.
Comments