一次Spring Transactional嵌套事务使用不同的rollbackFor的分析
起因:
项目期间由于一次异常回滚问题,发现自己在事务知识方面知识的遗漏,趁着这次机会,做了几次rollbackFor的测试。
测试:
现在有两个事务,事务oute包含事务Inner。事务A回滚规则是当事务抛出TestException,其中TestException继承RunTimeException。事务B的回滚规则是事务抛RuntimeException。事务的传播方式都是使用的默认,即 Propagation.REQUIRED。如以下代码:
@Override
@Transactional(rollbackFor = TestException.class)
public void transOuter() {
productMapper.updateOrderQuantityPessimistic(product_code1);
((ProductService) AopContext.currentProxy()).transInner();
} @Transactional(rollbackFor = Exception.class)
public void transInner() {
productMapper.updateOrderQuantityPessimistic(product_code);
if (true) {
throw new RuntimeException();
}
}
以下为TestException的代码。
public class TestException extends RuntimeException {
public TestException(String message) {
super(message);
}
}
一开始按照自己对事务的理解, 默认的传播属性之下。事务B启动的时候,会默认使用事务A的rollbackFor来进行回滚,所以该代码运行时候。程序不会回滚。
然而测试,测试完之后发现事务A、B都进行了回滚。
看着测试结果产生了疑问。难道是以innner的rollBack为准?接着进行测试。
@Override
@Transactional(rollbackFor = Exception.class)
public void transOuter() {
productMapper.updateOrderQuantityPessimistic(product_code1);
((ProductService) AopContext.currentProxy()).transInner();
} @Transactional(rollbackFor = TestException.class)
public void transInner() {
productMapper.updateOrderQuantityPessimistic(product_code);
if (true) {
throw new RuntimeException();
}
}
再次测试,测试完之后发现事务A、B依然进行了回滚。
感觉自己对事务的理解还是太浅薄了,是时候debug一波源码。
分析源码:
查看 org.springframework.transaction.interceptor.TransactionAspectSupport 类的 invokeWithinTransaction方法。该方法是事务执行的主要方法,这里我们主要看第20行的事务捕捉那一块。completeTransactionAfterThrowing的方法。
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable { // If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 事务异常捕捉主要在这边获取
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
} else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
}); // Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}

这边显示当事务的rollbackFor为TestException,而抛出的异常为RunTimeException时候。跟我们的transInner一致。接着往下看 completeTransactionAfterThrowing 方法。主要看第8行,第8行对事务进行判断,是否对该抛出的异常进行回滚。
protected void completeTransactionAfterThrowing(TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
//这里主要判断事务捕获了异常以后,是否进行回滚
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
}
再深入点进去看,看到第11行,这边获取该异常的深度。跳转到片段二进行代码查看。
public boolean rollbackOn(Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
}
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
if (this.rollbackRules != null) {
for (RollbackRuleAttribute rule : this.rollbackRules) {
//获取异常的深度?
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}
// User superclass behavior (rollback on unchecked) if no rule matches.
if (winner == null) {
logger.trace("No relevant rollback rule found: applying default rules");
//如果depth为-1之后,父类的回滚方式
return super.rollbackOn(ex);
}
return !(winner instanceof NoRollbackRuleAttribute);
}
根据深度代码查看,rollbackFor和抛出异常ex不一致,返回-1。再回去看上面的代码片段,当返回-1之后,代码走到第28行。进行父类的回滚方法。
private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
//此处RuntimeException 跟TestException不一致,返回-1
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
以下是父类的代码是否回滚判断方法,有没有很眼熟,只要抛出的异常的是RunTimeExcpetion或者Error则进行回滚。
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
总结
根据上面的代码,我们可以推断出以下几个结论:
1、当我们抛出的异常为RunTime及其子类或者Error和其子类的时候。不论rollbackFor的异常是啥,都会进行事务的回滚。
2、当我们抛出的异常不是RunTime及其子类或者Error和其子类的时候,必须根据rollbackfor进行回滚。比如rollbackfor=RuntimeException,而抛出IOException时候,事务是不进行回滚的。
3、当我们抛出的异常不是RunTime及其子类或者Error和其子类的时候,如果嵌套事务中,只要有一个rollbackfor允许回滚,则整个事务回滚。
经过测试,上述的结论也没发现什么问题。
一次Spring Transactional嵌套事务使用不同的rollbackFor的分析的更多相关文章
- 数据库事务中的隔离级别和锁+spring Transactional注解
数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题.ACID首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(At ...
- How does Spring @Transactional Really Work?--转
原文地址:http://blog.jhades.org/how-does-spring-transactional-really-work/ In this post we will do a dee ...
- Spring @Transactional使用的示例
Spring @Transactional使用的示例: 参考: http://blog.csdn.net/seng3018/article/details/6690527 http://blog.si ...
- Spring @Transactional 使用
Spring @Transactional是Spring提供的一个声明式事务,对代码的侵入性比较小,只需考虑业务逻辑,不需要把事务和业务搞混在一起. @Transactional 可以注解在inter ...
- Java:Spring @Transactional工作原理
本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: propagation(事务传播)和isolation(隔离性)等属性的使用 事务使用 ...
- spring @Transactional 事务注解
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE, rollbackFor = ...
- Spring @Transactional (一)
Spring @Transactional (一) 博客分类: JAVA SpringJPAJDBCUPSQL Spring事务的传播行为 在service类前加上@Transactional,声明 ...
- [转]数据库事务中的隔离级别和锁+spring Transactional注解
数据库事务中的隔离级别和锁 数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性.安全性也是我们需要研究的问题.ACID首先总结一下数据库事务正确执行的四个要素(ACID): 原子性(At ...
- 25.Spring @Transactional工作原理
转自:http://www.importnew.com/12300.html 本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: prop ...
随机推荐
- 记录常用的linux命令
原文链接:https://www.cnblogs.com/suger43894/p/11024594.html 系统信息相关 date 显示系统日期 cat /proc/mounts 显示已加载的文件 ...
- asp.net core Mvc 增删改查
1.创建项目 创建Data文件夹 创建实体类Students/cs public class Students { public Guid Id { get; set; } public string ...
- Faster-RCNN论文精读
State-of-the-art object detection networks depend on region proposal algorithms to hypothesize objec ...
- 【leetcode】328. Odd Even Linked List
题目如下: Given a singly linked list, group all odd nodes together followed by the even nodes. Please no ...
- thinkphp REST
REST介绍 REST(Representational State Transfer表述性状态转移)是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性.REST提出了一些设 ...
- AcWing 139. 回文子串的最大长度 hash打卡
如果一个字符串正着读和倒着读是一样的,则称它是回文的. 给定一个长度为N的字符串S,求他的最长回文子串的长度是多少. 输入格式 输入将包含最多30个测试用例,每个测试用例占一行,以最多1000000个 ...
- Centos7.4安装elasticsearch6.3+kibana6.3集群
Centos7.4安装elasticsearch+kibana集群 Centos7.4安装elasticsearch+kibana集群 主机环境 软件环境 主机规划 主机安装前准备 安装jdk1.8 ...
- VMware Pro v14.1.1 官方版本及激活密钥
热门虚拟机软件VMware Workstation Pro现已更新至14.1.1,14.0主要更新了诸多客户机操作系统版本,此外全面兼容Wind10创建者更新.12.0之后属于大型更新,专门为Win1 ...
- 洛谷 P3369 【模板】普通平衡树 (Treap)
题目链接:P3369 [模板]普通平衡树 题意 构造一种数据结构满足给出的 6 种操作. 思路 平衡树 平衡树的模板题. 先学习了一下 Treap. Treap 在插入结点时给该结点随机生成一个额外的 ...
- 剑指offer——72圆圈中最后剩下的数字
题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指 ...