阅读 316

@Transation在什么情况下会失效(下)| 小册免费学

@Transational开发中最常用的属性的由两个:propagation、rollbackFor两个属性值的默认值:

propagation的默认值为REQUIRED,方法A调用方法B,如果A有事务则加入A中事务。事务具体属性取决于在哪个方法中触发的。

rollbackFor:默认值为RuntimeExcepion和Error及其继承子类。

高频导致@Transational失效情景

1.同一个类中没有事务注解的方法调用有事务注解的方法,导致@Transational失效

2.异常被catch捕获了

对于第2种情况,比较好的处理办法是尽量不在Service层处理异常try catch,而是习惯抛出业务异常,让@RestControllerAdvise统一捕获并返回前端。

同一个类中没有事务注解的方法调用有事务注解的方法,导致@Transational失效,解决方法:

1.给没加事务注解的方法也加上事务注解。

同一个类中非事务方法调用事务方法失效的原因在于:非事务方法调用事务方法时调用的为this.xxx方法,而这里的this为普通为加强的方法,非加强了事务的代理对象。 解决方案:所以在非事务方法调用事务方法时,通过加强了事务的代理对象去调用即可。

2.从ApplicationContext中获取代理对象(事务加强后的对象)

3.通过@Autowired注入自身(得到的对象为加强了事务的代理对象)

4.引入aop依赖,使用@EnableAsceptJAutoProxy(exposeProxy=false),最后通过AopContext.currentProxy()获取到代理对象。

关于代理对象与this

同一个类中没有事务注解的方法调用有事务注解的方法失效。怎么使有事务注解的方法生效呢?

第一种方法在没有事务的方法上加事务注解,其实是让没有事务的方法加上事务,然后让被调用的事务注解方法加入到调用的事务方法中。其实并没有解决被调用的事务方法失效的问题,内部还是调用this.XXX方法,为未加强的普通对象的方法。当这个方法报错时会沿着方法链向上抛,直到传到有事务加强的方法时回滚。

Why?this不是代理加强对象呢?

动态代理的原理是,如InvocationHandler的invoke()方法中会使用target目标对象对象原方法,所以当target调用目标方法的里面时,还是目标对象的this。

Spring通过注解自动注入对象时,会将代理对象注入进去,当调用代理对象的方法时,最终会调用目标对象的方法。

事务是否生效基于以下两点:

1.是否有给方法做增强的事务注解或xml配置

2.调用时是否使用的是增强了事务处理的方法

代理对象为什么加了@Transational注解之后就会触发事务?

在AOP源码中一个个增加的方法被会被包装成一个个拦截器,放在拦截器链中。

1.当代理对象执行每个方法时,最终会导向CglibAopProxy的内部类DynamicAdvisedInterceptor的intercept方法,而在这个方法中会判断当前方法有没有要执行的拦截器链。

在第二点的判断条件中如果链为空且方法为public时,执行代理方法对象的invoke方法。所以注解放在private私有方法上也会失效。 2.当public方法加了事务注解,事务处理的代码就会被加入到拦截链中,最后会在加了事务注解的方法前后调用。

特别要注意,任何Java代码层面的事务控制其实还是依赖于setAutoCommit(false),也就是先关闭默认提交,此时MySQL底层就会通过日志把一连串操作先记录起来,最后一起提交。如果中间失败了,仍可根据日志回滚。具体实现细节可以去查阅MySQL事务相关资料。 在事务切面支持类中第二行通过目标方法和目标对象后去事务注解的属性然后通过属性,在开启事务时判断是否需要事务。rollbackFor,propagation等属性。

重新理解rollbackFor,propagation

结论:

1.并不是所有的异常都会回滚,所以最好指定异常类型 2.propagation是写给调用者看的,而不是给被调用者看的。 最好指定rollbackFor: 默认指定回滚异常类型为Error和RuntimeException propagation:

当test方法未加事务注解时,则当前test方法是没有事务的,所以当selectUser方法的传播属性设置为supports时,没有事务则以非事务运行。所以selectUser中也没有事务,最终调用this.updateUser时也导致没有事务了。调用方,即调用selectUser()的方法是否存在事务 propagation是写给调用者(test方法)看的,而不是写给被调用者(updateUser)看的 Propagation.REQUIRES_NEW

重新创建一个新的事务,和外面的事务相互独立。

本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情

文章分类
后端
文章标签