最近改造前两年的一个项目,一个版本发布的功能,需要操作3-4张表,使用的mybatis-plus做的持久化。
原有逻辑:
后台接收到publish请求,接入service1实现层。
service1方法添加了@Transactional执行方法:
1、add版本记录方法,默认发布版本状态为执行中
2、修改最新版本时间方法
3、service2的数据备份,由于该操作涉及三张表且数据量大比较耗时,所以在此方法上添加了@Async注解异步执行
service2数据备份方法中执行方法:
1、备份表1
2、备份表2
3、备份表3
4、修改版本发布状态为成功或失败
结果发现备份表3的操作发生异常前面操作均为回滚;
试着在service2的数据备份异步方法再加上@Transactional能保证几个备份表的操作和修改状态的操作在同一个事务下执行,但是还是不能和service1的方法同一个事务,因为主线程和子线程的在事务上是相互隔离的,子线程的异常不会影响主线程的事务混滚与否(让若主线程不主动throw出异常,子线程即使抛出了异常也不会影响主线程的);而且希望service2的方法中的第四步修改发布状态的操作不论前三步是否执行成功都执行,所以上面这样处理肯定不行。
优化方案:
把service1的发布方法改为异步执行加上@Async去掉@Transactional,其中调用service2的备份方法上去掉@Async加上@Transactional保证前三步备份步骤发生异常一起回滚,第四步修改发布状态独立出来提供一个方法加上@Transactional并且指定
propagation = Propagation.REQUIRES_NEW
1.标志REQUIRES_NEW会新开启事务,外层事务不会影响内部事务的提交/回滚 2.标志REQUIRES_NEW的内部事务的异常,会影响外部事务的回滚
这样就达到了想要的目的:不管备份是否成功,发布状态都会更新。