事务@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
就这样异常,所以不会自动回滚事务了。
1. 本站所有文章教程及资源素材均来源于网络与用户分享或为本站原创,仅限用于学习和研究。
2. 如果内容损害你的权益请联系客服QQ:1642748312给予处理。
码云笔记 » 事务@Transactional为什么会无效?