spring 事务传播行为以及失效原因

news2024/12/23 11:10:46

今天在查看以前写的代码时,看到了事务的使用,感觉自己对这一块并不是特别清晰,所以就系统的学习了一下。在学习过程中发现很多地方自己以前理解的还是有点不对,所以记录一下学习笔记,希望帮助到大家。

一、事务传播行为

备注:因为除了PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED,其他的都不是特别难以理解,所以我这里就只对这两个做了一下代码实例。

当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播,例如,方法可能继续在当前事务中执行,也可以开启一个新的事务,在自己的事务中执行。
声明式事务的传播行为可以通过 @Transactional 注解中的 propagation 属性来定义,比如说:

@Transactional(propagation = Propagation.REQUIRED)
public void savePosts(PostsParam postsParam) {
}

TransactionDefinition 一共定义了 7 种事务传播行为,其中PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW 两种传播行为是比较常用的。

PROPAGATION_REQUIRED

 这也是 @Transactional 默认的事务传播行为,指的是如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。更确切地意思是:如果外部方法没有开启事务的话,Propagation.REQUIRED 修饰的内部方法会开启自己的事务,且开启的事务相互独立,互不干扰。如果外部方法开启事务并且是 Propagation.REQUIRED 的话,所有 Propagation.REQUIRED 修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务都需要回滚。也就是说如果a方法和b方法都添加了注解,在默认传播模式下,a方法内部调用b方法,会把两个方法的事务合并为一个事务。

PROPAGATION_REQUIRES_NEW

创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法都会开启自己的事务。举例来说:

    @Resource
    ApplicationContext applicationContext;

    @Resource
    private IUserService userService;

    @GetMapping("insert")
    @Transactional(rollbackFor = Exception.class)
    public Result insertUser(){
        User user = new User();
        user.setPhone("11111801061").setNickName("111");
        userService.save(user);
        UserController bean = applicationContext.getBean(UserController.class);
        bean.saveAnother();
        int k = 10/0;
        return Result.ok();
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void saveAnother() {
        User user = new User();
        user.setPhone("11111801062").setNickName("222");
        userService.save(user);
    }

 在上面这个例子中,首先是insertUser()方法会开启一个事务,然后在调用到saveAnother()方法时,因为该方法的事务行为是Propagation.REQUIRES_NEW,所以它会在新开启一个事务,并且将原来的事务挂起。当saveAnother()方法的事务结束以后,insertUser()方法的事务恢复开始继续执行。

在上面的例子中,因为saveAnother()方法是独自开启了一个新的事务,所以它并不受原来事务的影响,它在事务提交以后才会恢复insertUser()的事务,所以在insertUser()中虽然抛出了异常,但是此时saveAnother()事务早已提交,所以它并不受insertUser()中抛出异常的影响。

但是如果反过来,把代码改成下面这种情况

 那么此时,因为saveAnother()在执行完成以后会将异常抛给insertUser(),所以insertUser()会将自己的事务进行回滚。这也就是很多博客说的A不会影响B但是B会影响A。

PROPAGATION_NESTED

如果当前存在事务,就在当前事务内执行;否则,就执行与 PROPAGATION_REQUIRED 类似的操作。我们用下面的例子来说明:

    @GetMapping("insert")
    @Transactional(rollbackFor = Exception.class)
    public Result insertUser(){
        User user = new User();
        user.setPhone("11111801061").setNickName("111");
        userService.save(user);
        UserController bean = applicationContext.getBean(UserController.class);
        bean.saveAnother();
        return Result.ok();
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
    public void saveAnother() {
        User user = new User();
        user.setPhone("11111801062").setNickName("222");
        int k = 10/0;
        userService.save(user);
    }

在上面的代码中,因为saveAnother()方法事务行为是Propagation.NESTED,所以它会嵌套在insertUser()方法的事务中执行。其实这个嵌套应该就类似于mysql中的savepoint,比如现在saveAnother()方法中产生了异常,那么就会将事务回滚到当前事务开始的地方。但是!!!!此时saveAnother()方法并不会提交自己的事务!!它的事务会跟insertUser()一起提交!!这也是PROPAGATION_REQUIRES_NEW的本质区别,它是相当于开了一个子事务,跟随父级事务一起提交,并不会先提交。所以当它发生了异常以后,会先回滚自己的方法到达,将数据恢复到它自己事务开启之前的状态。

如果父级对其异常进行了捕获,那么父级事务可以正常提交。

如果父级事务接到异常不进行捕获那么自己也会回滚自己的事务。

如果两个方法都没问题,那么最终数据会被一起commit。

如果在insertUser()中发生了异常,因为此时saveAnother()方法事务还没有提交,那么最终就会导致再最后两个事务都会被回滚。这也就是大家说的A会影响B但是B不会影响A。但是A还是能够正常接收到B的异常的,处理情况下才不会影响,不处理两个会一起回滚。

可能对mysql不太熟悉的朋友对这个概念有点陌生。其实就是在一个事务中假如有很多修改的地方,我可以在某一个修改点增加一个savepoint,然后在这个savepoint之后再去执行其余的数据修改。如果修改发生了问题我可以选择回滚到某一个savepoint,在这个savepoint之前修改的数据并不会被回滚。如果还是不太明白的话可以搜几篇文章看一下,应该就会很清晰了。

PROPAGATION_SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

PROPAGATION_NOT_SUPPORTED

 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

PROPAGATION_MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

PROPAGATION_NEVER

以非事务方式运行,如果当前存在事务,则抛出异常。

二、事务失效的情况

我觉得失效这个事情也很好理解,在排除数据库不支持的情况。其他情况应该就是你拿到的并不是spring aop生成的代理对象。熟悉spring IOC 的应该都清楚在spring创建Bean时如果检测到有事务就会提前进行aop生成代理对象。用于执行事务。所以如果说我们事务失效,那么首先判定你当前拿到的是不是代理对象。

举一个比较常见的例子:

    @GetMapping("insert")
    @Transactional(rollbackFor = Exception.class)
    public Result insertUser(){
        User user = new User();
        user.setPhone("11111801061").setNickName("111");
        userService.save(user);
        this.saveAnother();
        return Result.ok();
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
    public void saveAnother() {
        User user = new User();
        user.setPhone("11111801062").setNickName("222");
        int k = 10/0;
        userService.save(user);
    }

 在执行过程中下个断点很容易就能看到

 可以看到我们拿到的就是当前对象本身,并不是代理对象,所以此时它调用的方法上如果有事务,事务就会失效。

所以此时可以通过ApplicationContext或者注入(解决方案可以自己看一下,有很多种方式)来获取当前对象的代理类。

 可以看到这样拿到的就是当前类的代理类了,所以此时再去执行,被调用的方法事务就会生效。

1.数据库不支持事务。

Spring事务生效的前提是所连接的数据库要支持事务,如果底层的数据库都不支持事务,则Spring的事务肯定会失效。例如,如果使用的数据库为MySQL,并且选用了MyISAM存储引擎,则Spring的事务就会失效。

2.事务方法未被Spring管理。

    如果事务方法所在的类没有加载到Spring IOC容器中,也就是说,事务方法所在的类没有被Spring管理,则Spring事务会失效。

3.方法没有被public修饰。

    如果事务所在的方法没有被public修饰,此时Spring的事务会失效。

4.同一类中方法调用。

    如果同一个类中的两个方法分别为A和B,方法A上没有添加事务注解,方法B上添加了 @Transactional事务注解,方法A调用方法B,则方法B的事务会失效。

5.未配置事务管理器。

    如果在项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效。

6.方法的事务传播类型不支持事务。

    如果内部方法的事务传播类型为不支持事务的传播类型,则内部方法的事务在Spring中会失效。

7.不正确的捕获异常。

    不正确的捕获异常也会导致Spring的事务失效。

8.错误的标注异常类型。

    如果在@Transactional注解中标注了错误的异常类型,则Spring事务的回滚会失效。

希望对大家能够有所帮助!如有问题欢迎交流


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/71936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

随机接入流程 - 2-Step RA

Overview 在LTE和R15 NR中,终端以及基站采用的接入技术均为四步随机接入(4-Step Random Access)技术,即终端和基站之间需要经过5次信息交互(这里我们所说是的基于竞争的随机接入过程,对于非竞争随机接入过程只需要3次信息交互)才能完成随机接…

m基于FPGA的数字下变频verilog设计

目录 1.算法描述 2.仿真效果预览 3.verilog核心程序 4.完整FPGA 1.算法描述 整个数字下变频的基本结构如下所示 NCO使用CORDIC算法,CIC采用h结构的CIC滤波器,HBF采用复用结构的半带滤波器,而FIR则采用DA算法结构。 这里,我们…

华硕编程竞赛11月JAVA专场 B题召唤精灵 题解

作者主页:Designer 小郑 作者简介:软件工程师一枚,来自浙江宁波,负责开发管理公司OA项目,专注软件前后端开发(Vue、SpringBoot和微信小程序)、系统定制、远程技术指导。CSDN学院、蓝桥云课认证讲…

ATtiny13与Proteus仿真-GPIO与点亮LED仿真

GPIO与点亮LED仿真 1、GPIO介绍 ATtiny13的端口具有(Read-Modify-Write,读取-更改-写入)基础功能。这就意味着,可以通过 SBI 和 CBI 指令更改一个端口引脚的方向,而不会影响其他端口方向。同时,如果更改一个端口的值,也不会影响其他的端口的值。 ATtiny13的端口输出缓冲…

Spring boot前后端分离项目使用docker-compose部署在Ubuntu上 以及内网穿透,外网访问项目

docker-compose.yml部署vue Springboot前后端分离项目 以及内网穿透,外网访问项目一、windows上部署前后端项目,测试1、前端Vue打包,放到nginx的html中2、nginx代理转发后端端口:4、后端Springboot项目使用Maven打包二、Linux上使…

-aop-

文章目录一.动态代理实现invocationHandler二.AOP概述环绕通知Pointcut定义切入点一.动态代理实现invocationHandler 动态代理:可以在程序的执行过程中,创建代理对象。 通过代理对象执行方法,给目标类的方法增加额外的功能(功能增…

Filebeat采集数据到ES保证数据不重复

一.背景 业务需求使然,API接口负责收集用户传递上来的json数据,为了保证接口性能和数据的可靠性。我们没有直接拿到数据,然后存储到mysql或者kafka,而是直接使用最稳妥的方式,写文件。之后采用filebeat对数据文件进行采…

8_1、Java基本语法之多线程基本概念

一、涉及到多线程的基本概念 1、程序(programm) 概念:是为完成特定任务、用某种语言编写的一组指令的集合。即指一 段静态的代码,静态对象。 2、进程(process) 概念:是程序的一次执行过程,或是正在运行的一个程…

Cy7 Tyramide, Tyramide-Cy7,花青素Cy7 酪酰胺化学试剂供应

一:产品描述 1、名称 英文:Cyanine7 Tyramide,Cy7 Tyramide,Tyramide-Cy7 中文:花青素Cy7 酪酰胺 2、CAS编号:N/A 3、所属分类:Cyanine 4、分子量:802.01 5、分子式&#xff…

Docker学习6-Docker镜像commit操作案例

在上一篇中,我们知道了docker是基于联合文件系统的分层镜像。而且也知道了镜像是只读的,容器才是可以写的。那么,如果我们要修改镜像,修改之后,怎么提交呢?本文,凯哥将介绍,docker的…

svd分解

sklearn中svd分解 class sklearn.decomposition.TruncatedSVD(n_components2, *, algorithmrandomized, n_iter5, random_stateNone, tol0.0) 参数: n_components:整数,默认2 输出数据的所需维度。必须严格小于特征数。默认值对可视化很有用…

Git 操作 Gitea 出现 kex_exchange_identification: Connection closed by remote host

1. 问题背景 我的部分代码储存在使用 Gitea 搭建的服务上: DevWiki - 首页 - DevWiki Gitea 之前都是使用网页在网站上进行 仓库管理, 今天在clone代码的时候出现异常: kex_exchange_identification: Connection closed by remote host, 具体以下提示: $ git clone gitgit.d…

【案例分享】跨机房ES同步实战

背景 众所周知单个机房在出现不可抗拒的问题(如断电、断网等因素)时,会导致无法正常提供服务,会对业务造成潜在的损失。所以在协同办公领域,一种可以基于同城或异地多活机制的高可用设计,在保障数据一致性…

微软CTO谈AI:逃不掉马拉松就准备好跑鞋!30秒自动化妆机;ChatGPT刷爆票圈;剪纸风格的AI绘画 | ShowMeAI资讯日报

👀日报合辑 | 🎡AI应用与工具大全 | 🔔公众号资料下载 | 🍩韩信子 📢 对话微软 CTO 凯文斯科特 (Kevin Scott):人工智能将去向何方? https://blogs.microsoft.com/ai/a-conversation-with-kevi…

【卡塔尔世界杯】空调制冷,全是科技与狠活

半自动化越位技术比赛用球AI Rihla球场智能空调Feelix Palm辅助技术可持续利用的体育场便利的数字设施和App 西班牙队和英格兰队穿外套出场,卡塔尔的空调功率到底有多大? 还是很大的,不管是室外还是室内,到处都安装了空调&#…

Verilog系统函数

Verilog系统函数前言一、$width(一)简介(二)$width 参数(三)例子二、Specify参数三、$display(一)简介(二)格式说明(三)例子1.例12.例…

Bert论文解读及相关代码实践

Bert:Bidirectional Encoder Representations from Transformers Transformer中双向Encoder表达学习。BERT被设计为通过在所有层中对左右上下文进行联合调节,从未标记文本中预训练深度双向表示。预训练的BERT模型可以通过仅一个额外的输出层进行微调&am…

AVS-试听分割-论文阅读

题目: Audio-Visual Segmentation 论文地址:https://arxiv.org/abs/2207.05042 GitHub地址:https://github.com/OpenNLPLab/AVSBench 项目主页:https://opennlplab.github.io/AVSBench/ 相关博客https://arxiv.org/abs/2203.03821 摘要 We propose to explore a new pro…

(附源码)ssm汽车租赁 毕业设计 271621

基于ssm的汽车租赁平台的设计与实现 摘 要 随着社会经济的快速发展,我国机动车保有量大幅增加,城市交通问题日益严重。为缓解用户停车难问题,本文设计并实现了汽车租赁平台.该系统通过错峰停车达到车位利用率最大化.基于现状分析,本文结合实际停车问题,从系统应用流程,系统软硬…

微电网和直流电网中最优潮流(OPF)的凸优化(Matlab代码实现)

📋📋📋本文目录如下:⛳️⛳️⛳️ 目录 1 概述 2 最优潮流 3 电力系统强大的CVX 4 直流电网中最优潮流(OPF)的凸优化 4.1 Matlab代码 4.2 运行结果 5 微电网中最优潮流(OPF)的凸优…