当前位置: 首页 > 科技观察

Spring事务失败场景总结

时间:2023-03-14 00:57:58 科技观察

项目使用Spring的@Transactional注解来控制事务,在使用过程中经常会出现事物不生效的场景。本文仅限于日常项目开发的总结。总结了以下几点,以供日后参考检查;可能不完整,但希望对有需要的同学有所帮助,避免踩坑。数据库引擎不支持东西。这里以MySQL为例。它的MyISAM引擎不支持事务操作。InnoDB是支持事务的引擎。一般使用InnoDB来支持事务。根据MySQL官方文档:https://dev.mysql.com/doc/refman/5.5/en/storage-engine-setting.html从MySQL5.5.5开始默认的存储引擎是:InnoDB,之前默认是:MyISAM,所以值得注意的是底层引擎不支持事务。不受Spring管理//@Service(这个注解不能去掉)publicclassAccountServiceImplimplementsAccountService{@Transactionalpublicvoidinsert(Accountaccount){//insertaccount}}如果此时注释掉@Service注解,这个类不会如果作为Bean加载,那么这个类就不会被Spring管理,事务自然也就失效了。以下方法未在Spring官方文档中公开:使用代理时,您应该仅将@Transactional注释应用于具有公开可见性的方法。如果您使用@Transactional注释对受保护的、私有的或包可见的方法进行注释,则不会引发错误,但注释的方法不会显示已配置的事务设置。如果您需要注释非公共方法,请考虑使用AspectJ(见下文)。也就是说@Transactional只能用在public方法上,否则transaction不会Invalid,如果想用在非public方法上,可以考虑开启AspectJ代理模式。自调用问题见如下代码@ServicepublicclassAccountServiceImplimplementsAccountService{publicvoidinsert(Accountaccount){insertAccount(account);}@TransactionalpublicvoidinsertAccount(Accountaccount){//insertaccount}}没有@Transactionalinsert方法上的注解,调用了@Transactional注解的insertAccount方法,insertAccount方法上的事务实际上不起作用。看下面的代码@ServicepublicclassAccountServiceImplimplementsAccountService{@Transactionalpublicvoidinsert(Accountaccount){insertAccount(account);}@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidinsertAccount(Accountaccount){//insertaccount}}这次在insert方法中添加了@Transactional,在insertAccount中添加了REQUIRES_NEW来开启新的事务,那么新开启的事务是否起作用呢?这两个例子的答案是:行不通!因为他们自己调用,调用这个类自己的方法,不经过Spring的代理类,默认只有在外部调用事务时才会生效。这也是一个经常被人津津乐道的经典问题。数据源没有配置事务管理器@BeanpublicPlatformTransactionManagertransactionManager(DataSourcedataSource){returnnewDataSourceTransactionManager(dataSource);}如上图,如果当前数据源没有配置事务管理器,还是会失败!不支持@ServicepublicclassAccountServiceImplimplementsAccountService{@Transactionalpublicvoidinsert(Accountaccount){insertAccount(account);}@Transactional(propagation=Propagation.NOT_SUPPORTED)publicvoidinsertAccount(Accountaccount){//insertaccount}}Propagation.NOT_SUPPORTED:表示不带事务运行,如果当前有事务,则挂起。具体可以参考InnoDB的事务隔离级别和传播机制。他们都主动不支持事务操作,所以事务生效也没用!这是比较常见的异常被吃掉的场景@Service(这个注解不能去掉)然后不扔掉,事务不能回滚!异常类型错误@Service(这个注解不能去掉)这种事务也是无效的,因为默认回滚是:RuntimeException,如果要触发其他异常回滚,需要在注解上配置,如:@Transactional(rollbackFor=Exception.class)这个配置仅限于Throwable异常类及其子类。查资料,注意其他故障场景:1)比如导入文件到数据库时,使用多线程控制;可以参考查询spring多线程事务的问题2)SpringBoot+Shiro导致的事务失败总结这篇文章总结了事务失败的几种场景,其实最常见的是自调用,异常被吃掉,以及异常抛出类型错误。正如文章开头所说,本文不一定总结所有。它只是根据经验对常见的交易失败场景进行总结。如有遗漏,欢迎留言分享。