使用java的completablefuture怎么回滚事务?

二、异步某线程失败时,主线程回滚所有异步线程的事务!想要保证事务,肯定是使用@Transactional来实现。现在的场景是导入若干个大的Excel文件数据,因为每个Excel导入的表不同,所以只要保证单Excel的事务即可。上文中,是使用异步批量读取并插入的方式实现的Excel文件入库。也就是说,1个主线程事务 + 若干...
使用java的completablefuture怎么回滚事务?
大家好,我是哪吒。

一、前情提要

在上一篇文章中,我们通过双异步的方式导入了10万行的Excel,有个小伙伴在评论区问我,如果保证事务呢,如果分批的话。

原始需求:读取一个10万行的Excel

通过串行读取Excel,单个Excel耗时191s。

优化1:使用双异步后,从191s优化到2s
优化2:使用双异步后,如何保证数据一致性?

通过Future获取异步返回值,再和Excel文件数据行进行比较,实现对数据准确性的判断!

优化3:获取双异步返回值时,如何保证主线程不阻塞?

Java8中引入了CompletableFuture,它实现了对Future的全面升级,可以通过回调的方式,获取异步线程返回值。

CompletableFuture的异步执行通过ForkJoinPool实现,它使用守护线程去执行任务。

ForkJoinPool在于可以充分利用多核CPU的优势,把一个任务拆分成多个小任务,把多个小任务放到多个CPU上并行执行,当多个小任务执行完毕后,再将其执行结果合并起来。

二、异步某线程失败时,主线程回滚所有异步线程的事务!

想要保证事务,肯定是使用@Transactional来实现。

现在的场景是导入若干个大的Excel文件数据,因为每个Excel导入的表不同,所以只要保证单Excel的事务即可。

上文中,是使用异步批量读取并插入的方式实现的Excel文件入库。

也就是说,1个主线程事务 + 若干个子线程事务,我们想要保证单Excel的插入事务,所有异步子线程有任何一个报错,都要进行事务回滚,如果全部都没报错,则进行事务提交。

这个时候,有的小伙伴可能会想到,主线程加个@Transactional注解,所有子线程分别加@Transactional注解,就可以了吧?

但是,这样是不行的,子线程的异常只会回滚其自身的事务。

如果Excel中有10万条数据,一次插入4200条数据,最后一次插入3400条。如果其它线程都插入成功了,最后一个报错了,此时,数据库中还是会有96600条数据插入成功,与单Excel的事务需求不符。

通过代码模拟这种情况:

三、@Transactional注解

声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

简而言之,@Transactional注解在代码执行出错的时候能够进行事务的回滚。

1、@Transactional

使用@Transactional后,当程序发生RuntimeException运行时异常在没有使用try,catch进行捕获的时候,程序都会中止,当程序发生中止,则会触发数据库的回滚。

当使用了trycatch进行捕获到这个异常,假如在catch中加入了throw e抛出异常,则程序中止,数据库回滚。

加入在try catch中没有throw e 抛出异常,只是简单的打印异常,则异常被捕获未抛出异常去终止程序,在trycatch中的操作数据库语句插入失败,在trycatch上面和下面的数据库相关插入语句成功,也就是程序成功跑完,数据库不会发生回滚。

2、@Transactional(rollbackFor = Exception.class)

在@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。

四、注解失效问题
1、@Transactional应用在非public修饰的方法上

事务拦截器在目标方法执行前后进行拦截,内部会调用方法来获取Transactional注解的事务配置信息,调用前会检查目标方法的修饰符是否为public,不是public则不会获取@Transactional的属性配置信息。

2、@Transactional注解属性rollbackFor设置错误

rollbackFor可以指定能够触发事务回滚的异常类型。

Spring默认抛出了未检查unchecked异常(继承自RuntimeException的异常)或者Error才回滚事务;其他异常不会触发回滚事务。

如果在事务中抛出其他类型的异常,但却期望Spring能够回滚事务,就需要指定rollbackFor属性。

3、同一个类中方法调用,导致@Transactional失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

在同一个类中调用异步方法,等于调用this本类的方法,没有走Spring生成的代理类,也就不会让他异步执行,@Transactional的原理也类似。

4、捕获异常

如果你手动的catch捕获这个异常并进行处理,事务管理器会认为当前事务应该正常commit,就会导致注解失效,如果非要捕获且不失效,就必须在代码块内throw new Exception抛出异常。

五、通过Future获取异步返回值,添加事务
1、添加事务(1)添加事务 + 不开启异步

如果入库异常,事务回滚成功

(2)添加事务 + 开启异步

回滚失败!

2、手动添加事务(1)添加事务 + 不开启异步

如果入库异常,事务回滚成功

(2)Future获取异步返回值,添加手动事务,异常回滚失败!

六、@async + @Transactional事务失效问题
回顾一下需求:异步某线程失败时,主线程回滚所有异步线程的事务!

是代码有问题,还是就是实现不了呢?

@Async和@Transactional注解都是通过Spring aop实现的,核心都是靠着关键的MethodInterceptor实现,@Async会给对应bean代理对象中放入一个AnnotationAsyncExecutionInterceptor拦截器,而@Transactional会给对应bean的代理对象中放入一个TransactionInterceptor拦截器。

Spring事务管理的传播机制是使用ThreadLocal实现的。因为ThreadLocal是线程私有的,所以Spring的事务传播机制是不能够跨线程的。

七、Spring的事务传播机制是不能够跨线程的
1、一个异步线程一个事务,然后根据结果统一提交/回滚?
2、核心代码
3、异步线程类
4、事务复制类
5、为何要用事务复制类?而最后提交和回滚的时候也没用它?

如何不加会怎么样?

在提交和回滚的时候,会出现异常:

八、总结

经过不懈的努力,终于解决了“异步某线程失败时,主线程回滚所有异步线程的事务!”这个看起来很简单的问题。

也是对双异步入库系列的一个完结。

通过添加事务,可以有效的控制Excel异步插入数据的准确性。
读取一个10万行的Excel的最佳解决方案是:2024-11-19
mengvlog 阅读 8 次 更新于 2025-07-19 13:12:00 我来答关注问题0
  •  百度网友f114f56 java中的报错complete trystatement翻译成中文是什么意思?

    好像是:完全的尝试造句

  •  觅糖 flowable多实例会签如何在java中控制结束

    这个集合可以是一个列表或表达式,用于确定多个参与者的数量。3、控制子实例完成:在Java代码中,在每个子实例(即参与者)完成任务后,需要通过Flowable的API来标记该子实例的完成状态。您可以使用taskService.complete(taskId)来完成子实例的任务。4、监控多实例会签:可以使用Flowable提供的监控机制,例如...

  •  校企律说法 Java中出现 Syntax error, insert "while ( Expression ) ;" to complete DoStatement

    在Java编程过程中,如果遇到Syntax error, insert "while ( Expression ) ;" to complete DoStatement的问题,这通常意味着代码中缺少了必要的花括号{}。这可能是由于在编写代码时,没有正确地使用花括号来界定代码块,导致编译器无法正确识别代码的结构。使用Eclipse的快捷键Ctrl+Shift+F进行代码格式化,...

  •  武汉誉祥科技 怎么在windows 10 中安装 antlr4

    具体如下:1. 第一步,在下图所示的ANTLR的说明Get Started中,我们就可以看到安装的解释说明了,可以看到ANTLR Tool是一样而且必须的,但是runtime的安装则会随着使用target的语言的变化而变化,如果小伙伴们使用的是java,那么一个jar中就包括了ANTLR的tool和java runtime,就不需要另外安装了。2. 第...

  •  小李子学习帮 Java中出现 Syntax error, insert "while ( Expression ) ;" to complete DoStatement

    你这肯定是多了或少了花括号{}了 使用eclipse快捷键 ctrl+shift+f格式化代码方便排错。写代码前先写花括号再写代码,这才是一个良好的编码习惯

檬味博客在线解答立即免费咨询

Java相关话题

Copyright © 2023 WWW.MENGVLOG.COM - 檬味博客
返回顶部