没错,我又被事务坑了!
即上次的mq发送消息之后,业务代码回滚,导致发了一条中奖消息给用户!!,这次又被spring的事务坑了
这次是这样的,一个兑奖接口进来,我们先改变了这条数据的状态为已兑奖,之后我们就调用其他微服务的rpc方法,但是,rpc方法是会抛出个受检查的异常的,所以我们必须捕获,但是我们兑奖的方法上面又加了个@Transactional注解来控制事务,代码类似这样的:
@Override
@Transactional
public Result update(DTO dto) {
//更换状态
Mapper.updateStatusById( 1 , dto.getId());
String lessonWareAttrIds = null;
try {
//调用其他服务rpc接口
} catch (RpcException e) {
log.error("兑奖失败", e);
}
return Result.success();
}
嘿嘿嘿,您猜结果怎么着,rpc方法调用失败,但是表状态被变了,还好我及时改了过来,保住了我的绩效。
所以这次我们来谈谈spring 中的@Transactional注解事务
事务失效
首先我们要知道@Transactional在什么情况下会失效
第一种:自己处理掉了
第一种就像我们的案例一样,如果你把异常捕获到了,那么事务自然也就不生效了。因为事务就是要告诉spring,我发送异常了,我处理不了,我要抛给你,你自己看着办吧,但是你一旦catch掉了,说明你自己可以解决,那么spring自然也不会接管你的事务。
其次我们就要知道,spring事务的实现原理是什么:Spring框架中的事务管理主要通过AOP(面向切面编程)机制来实现,而AOP大家已经很熟悉了,那就是底层其实是通过代理去实现的,也就是说,如果没有被spring代理,那么这个事务自然就不会成功,例如下面的几种情况:
第二种:不是spring的bean
不是spring的bean,那自然就不会走spring代理,spring也无法通过aop去处理它,这个很好理解吧,那么这种情况也会失效:
第三种:自己调用自己的方法
接触过代理模式的都知道:都是先调用代理类的方法,再在代理类方法里面调用原方法,什么?你居然不知道代理模式?
如果不知道也没关系,现在就去恶补之前博主写的文章:【设计模式】我终于读懂了代理模式。。。
自己调用自己的方法,这种情况就属于没用通过代理类去调用test2方法,是自己去调用的,根据我们上面说的事务的原理,这样的写法自然而然就不会走事务了。。
第四种:方法设置为final关键字
我们可以看到,这里如果方法是final的话也是不会走代理方法的,原理和上面的类似,如果没被代理,则不会走事务
第五种,方法不为public
默认情况下,@Transactional 注解只对public方法有效。这是因为Spring的AOP(面向切面编程)代理默认使用的是基于接口的代理(JDK动态代理),这种代理只能拦截到public方法。但是要注意,如果使用CGLIB代理,其实也是可以用的,但是为了避免这种情况,还是为public较好哦。
那么上面呢除了第一种情况,基本就是没走代理所出现的事务失效的几种情况了。
第六种:不是默认的异常
Java的异常体系结构以Throwable为根,分为Error和Exception两大类,其中Exception又分为编译时异常(Checked Exceptions)和运行时异常(Unchecked Exceptions)。
@Transactional注解默认情况下只处理未检查异常(unchecked exceptions),也就是运行时异常(RuntimeException及其子类)。
如果你了解mysql的事务你就知道,在学习spring的事务中,其实就是对应的mysql的事务,如果你把mysql的事务先学习了,那么spring的事务其实也就那么一回事。
什么?你居然不知道mysql的事务?建议去恶补一下mysql的事务,或者读一读作者之前写过的:
你不能不知道的脏写,脏读,不可重复读,幻读超级详细解读
这里面就是对于数据库的操作就是一个事务。只有全部成功或者全部失败。
在一个方法上面添加了@Transactional注解,你可以理解的操作就是这一个方法就是一个mysql里面的事务了,这么理解是不是豁然开朗了,如同醍醐灌(不是)
那么一个方法是在一个线程里面去跑的,那么多线程是不是就是在多线程里面跑了(废话),所以引出了下面的情况:
第七种:多线程情况下
这个时候如果c类里面的方法回滚了,那么这个test方法也是不会回滚的,回到我们开始说的:一个线程一个事务,是不是就可以理解了,那都不是一个事务,所以就不会一起回滚了。
第八种:mysql存储引擎不为innodb
mysql的存储引擎:
MyISAM:不支持事务,使用表级锁,适合读多写少的场景。
InnoDB:支持事务,使用行级锁,适合高并发和需要事务处理的场景。
上面我们又说了,可以把spring的事务看做是mysql的事务,如果你要操作数据库的话,那你本身数据库不支持事务,那么spring配置了事务也没用。Spring的事务管理和数据库的事务支持是紧密相关的。有句话说的好:巧妇难为无米之炊
以上就是博主列举了几个事务会失效的常见的坑,比市面上其他的教程讲的更通俗易懂吧(我是这么觉得的),下次我们来讲一下Spring的事务传播性。大家在开发过程中应该避免踩这些个坑,在开发过程中别像博主一样犯第一总错误,到时候自己的绩效又变成了C,那就两行泪了。