事务@Transactional为什么会无效?
导致 @Transactional 无效的常见情况有以下 5 个:
- 非公共物质的方法;
timeout初始化时间设置过小;- 代码中使用
try/catch处理异常; - 调用类内部的
@Transactional方法; - 不支持数据库事务。
很多人只知道答案但不知道原因,这就像只谈恋爱不结婚一样,是不能同意接受的,所以本篇我们就来讨论一下,导致事务无效的背后原因到底是什么?
在 5 种场景中,第 2 种情况(数据库讨论时间设置过小)和第 5 种不支持事务),我们这里不再赘述了,这里重点介绍我们来其他 3 种情况。
1. 非公共物质的方法
非公共事务的方法上,即使加了事务@Transactional仍然不会生效动态,原因是因为@Transactional使用是 Spring AOP 实现的,而 Spring AOP 是通过代理的,而@Transactional在生成代理时会判断,如果方法为物质的方法生成代理对象,则这样也不会公开自动执行事务了,它的部分源如下:
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
// 非 public 方法,设置为 null
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// 后面代码省略....
}
2.try/catch 导致事务无效
@Transactional 执行流程是:@Transactional 会在方法执行前,会自动开启事务;在方法成功执行完成,会自动提交事务;如果方法在执行过程中,出现异常,那么它会自动回滚事务。
然而在方法中自己不会尝试追赶之后,事务就不会滚了,怎么会呢?造成这个问题的原因和自动回复的原因有@Transactional的原因,它的原因是:
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) {
// target invocation exception
// 异常时,在 catch 逻辑中,自动回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 自动提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// .....
}
}
我们可以通过 Transactional 从当执行的方法中出现异常,而@Transactional却出现了异常,然后再执行事务,然后当开发者自行执行事务处理之后,@Transactional 就在上面发现/发现异常了,那样就不会触发事务的自动回滚了,这就是为什么当@Transactional 发生 try/catch 之后就不会自动回滚(事务)的原因。
3.调用类内用的@Transactional 方法
当调用代码类内部的 @Transactional 物质的方法时,事务也不会生效,如下所示:
@RequestMapping("/save")
public int saveMappping(UserInfo userInfo) {
return save(userInfo);
}
@Transactional
public int save(UserInfo userInfo) {
// 非空效验
if (userInfo == null ||
!StringUtils.hasLength(userInfo.getUsername()) ||
!StringUtils.hasLength(userInfo.getPassword()))
return 0;
int result = userService.save(userInfo);
int num = 10 / 0; // 此处设置一个异常
return result;
}
上面的代码在添加用户之后发生了异常,程序也没有执行回滚,这是因为@Transactional是基于 Spring AOP 实现的,而 Spring AOP 又是基于动态代理实现的,而当调用类内部的方法时,不是通过对象完成的,而是通过对象实现的,这样事务就成为了代理对象,理所当然的就生效了。
总结
非公共事务的方法在@Transactional实现时因为没有做证明,如果是非公共的不会生成代理对象,所以就无效了;而调用类内部的@Transactional对象的方法时,也是成功调用代理对象,是调用调用方法,所以事务处理也可以这样,因为遇到开发者自定义的尝试进行事务处理;@Transaction 没有通过,但是不会通过/异常滚到的自动处理如果自定义用户了try/catch,@Transactional就这样异常,所以不会自动回滚事务了。
以上关于事务@Transactional为什么会无效?的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 事务@Transactional为什么会无效?
微信
支付宝