转自:https://blog.csdn.net/m0_38027656/article/details/84190949

写这篇文章的初衷呢就是最近遇到了一个spring事务的大坑.与其说是坑,还不如说是自己事务这块儿太薄弱导致的(自嘲下).

项目环境 sprinigboot

下面开始问题描述,发生的过程有点长,想直接看方案的直接跳过哦~;

最近在做项目中有个业务是每天定时更新xx的数据,某条记录更新中数据出错,不影响整体数据,只需记录下来并回滚当条记录所关联的表数据; 好啊,这个简单,接到任务后,楼主我三下五除二就写完了,由于这个业务还是有些麻烦,我就在一个service里拆成了两个方法去执行,一个方法(A)是查询数据与验证组装数据,另外一个方法(B)更新这条数据所对应的表(执行的时候是方法A中调用方法B);由于这个数据是循环更新,所以我想的是,一条数据更新失败直接回滚此条数据就是,不会影响其他数据,其他的照常更新,所以我就在方法B上加了事务,方法A没有加; 以为很完美,自测一下正常,ok通过,再测试一下报错情况,是否回滚,一测,没回滚,懵圈儿?.以为代码写错了,改了几处地方,再测了几次,均没回滚.这下是真难受了.

好啦,写到这里,相信各位看官心里肯定在嘲讽老弟了,spring的传播机制都没搞明白(/难受);

下面开始一步步分析解决问题:

  • PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

spring默认的是PROPAGATION_REQUIRED机制,如果方法A标注了注解@Transactional 是完全没问题的,执行的时候传播给方法B,因为方法A开启了事务,线程内的connection的属性autoCommit=false,并且执行到方法B时,事务传播依然是生效的,得到的还是方法A的connection,autoCommit还是为false,所以事务生效;反之,如果方法A没有注解@Transactional 时,是不受事务管理的,autoCommit=true,那么传播给方法B的也为true,执行完自动提交,即使B标注了@Transactional ;

在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务.是因为spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!

所以以上就是为什么我在没有标注事务注解的方法A里去调用标注有事务注解的方法B而没有事务滚回的原因;

看到这里,有的看官可能在想,你在方法A上标个注解不就完了吗?为什么非要标注在方法B上?

由于我这里是循环更新数据,调用一次方法B就更新一次数据,涉及到几张表,需要执行几条update sql, 一条数据更新失败不影响所有数据,所以说一条数据更新执行完毕后就提交一次事务,如果标注在方法A上,要所有的都执行完毕了才提交事务,这样子是有问题滴.

下边先上下代码:

方法B处理失败手动抛出异常触发回滚:

方法A调用方法B:

从上图可以看到,如果方法B中User更新出错后需要回滚RedPacket数据,所以User更新失败就抛出了继承自RuntimeException的自定义异常,并且在调用方把这个异常catch到重新抛出,触发事务回滚,但是并没有执行;

下面是解决方案:

1.把方法B抽离到另外一个XXService中去,并且在这个Service中注入XXService,使用XXService调用方法B;

显然,这种方式一点也不优雅,且要产生很多冗余文件,看起来很烦,实际开发中也几乎没人这么做吧?.反正我不建议采用此方案;

2.通过在方法内部获得当前类代理对象的方式,通过代理对象调用方法B

上面说了:动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!

所以我们就使用代理对象来调用,就会触发事务;

综上解决方案,我觉得第二种方式简直方便到炸. 那怎么获取代理对象呢? 这里提供两种方式:

1.使用 ApplicationContext 上下文对象获取该对象;

2.使用 AopContext.currentProxy() 获取代理对象,但是需要配置exposeProxy=true

我这里使用的是第二种解决方案,具体操作如下:

springboot启动类加上注解:@EnableAspectJAutoProxy(exposeProxy = true)

Spring事务—方法调用事务回滚相关推荐

  1. Spring中同一个类中方法调用事务不生效,非事务方法调用事务方式事务不生效

    我们假定在SerivceXXX中有两个方法: serviceA 非事务方法 serviceB事务方法 如果serviceA中方法定义类似如下: public void serviceA(){..... ...

  2. spring同类方法调用事务使用说明

    2019独角兽企业重金招聘Python工程师标准>>> #spring声明式事务使用说明: #前提: 1.springboot 2.0.3.RELEASE 2.application ...

  3. Spring事务管理中异常回滚知识点总结

    记录总结Spring核心知识点:事务使用与它的传播机制 目录 前言 问题场景 Spring 管理事务的原理 MySQL中的事务管理 JDBC中的事务管理 Spring 中的事务管理 Spring中的事 ...

  4. Spring事务总结(一) 内部调用事务失效、异常回滚

    Spring事务总结(一) 内部调用事务失效.异常回滚 参考文章: (1)Spring事务总结(一) 内部调用事务失效.异常回滚 (2)https://www.cnblogs.com/gss128/p ...

  5. Spring transaction事务之roll back回滚: rollback-for

    试验方法: 写一个单元测试,调用一个service层方法(发生对数据库进行写操作的方法--insert.update.delete)即可. applicationContext.xml 样子(如何设置 ...

  6. java jdbc 回滚_java_详解Java的JDBC API中事务的提交和回滚,如果JDBC连接是在自动提交模式 - phpStudy...

    详解Java的JDBC API中事务的提交和回滚 如果JDBC连接是在自动提交模式下,它在默认情况下,那么每个SQL语句都是在其完成时提交到数据库. 这可能是对简单的应用程序,但有三个原因,你可能想关 ...

  7. Java-JDBC【之】事务介绍、事务特性、操作事务(事务提交、异常回滚)

    Java-JDBC[之]事务介绍.事务特性.操作事务(事务提交.异常回滚) 1.数据库事务 1.1.介绍 1.2.事务特性(ACID) 1.3.隔离性(Isolation),带来的问题与处理 1.4. ...

  8. java pg数据库事务回滚,基于Postgresql 事务的提交与回滚解析

    用过oracle或mysql的人都知道在sqlplus或mysql中,做一个dml语句,如果发现做错了,还可以rollback;掉,但在PostgreSQL的psql中,如果执行一个dml,没有先运行 ...

  9. oracle表结构修改回滚,87.Oracle数据库SQL开发之 修改表内存——数据库事务的提交和回滚...

    87.Oracle数据库SQL开发之 修改表内存--数据库事务的提交和回滚 数据库事务(transaction)就是一组SQL语句,这组SQL语句时一个逻辑工作单元. 要永久性的记录事务中SQL语句的 ...

最新文章

  1. C#中协变和逆变的基本概念、List和List.Select方法的命名空间
  2. 一天搞定CSS: 浮动(float)与inline-block的区别--11
  3. 本科、硕士、博士,究竟有何区别?
  4. boot.oat FC问题分析报告
  5. React开发(157):一级直接用getFieldDecorator
  6. Java达到MySQL数据库备份(两)
  7. 【人脸识别】基于matlab GUI BP神经网络人脸识别(含识别率)【含Matlab源码 891期】
  8. /dev/null空字符设备文件
  9. 笨方法学Python-1
  10. raid卷的作用_硬盘raid功能是什么意思,有何作用
  11. zlog日志使用说明
  12. [网络安全自学篇] 三十五.恶意代码攻击溯源及恶意样本分析
  13. Win11桌面切换快捷键是什么?Win11快速切换桌面的方法
  14. C语言中runtime错误,runtime error错误解决方案 打开软件出现runtime error
  15. 32位系统为什么又叫x86系统呢?
  16. 《深度学习之PyTorch物体检测实战》—读书笔记
  17. c语言 宏do while,关于C语言宏定义 使用do{ xxxx }while()
  18. foxmail怎么群发邮件
  19. workflow 的审批流程的业务实现一
  20. 企业微信话术库是什么?如何添加?

热门文章

  1. @@ERROR 变量
  2. 大型网站的架构设计问题—-大型高并发高负载网站的系
  3. F#基本类型——Records
  4. div+css 布局浏览器兼容
  5. SpringBoot集成MyBatis-Plus分页插件
  6. Springboot实现邮件发送(2020最新版)
  7. Spring对不同来源的Resources的支持
  8. python snmp采集交换机信息_网管交换机与非网管交换机的利弊介绍
  9. 【题解】牛客小白月赛16(部分题,待补充……)
  10. lstm中look_back的大小选择_使用PyTorch手写代码从头构建LSTM,更深度的理解其工作原理