【SpringBoot实践】事务和事务传播机制失效原因正确使用事务的建议

news2024/11/24 16:59:20

文章目录

  • 1.概述
  • 2.事务与事务传播
    • 2.1 声明式事务说明
    • 2.2.声明式事务失效原因
    • 2.3.事务的传播机制
    • 2.4.事务传播失效原因
  • 3.事务使用建议
  • 4.总结

1.概述

我们在开发工作中经常会使用到事务,来保证数据库做增、删、改操作时的数据一致性,在使用Spring来处理事务的时候,如果没有正确的使用就很易容出现事务失效或者事务传播失效的问题,导致预期结果与实际结果不符。
为了彻底解决这种“一不小心”出现的事务失效问题,今天就结合理论和代码实践,验证一下什么情况下会导致事务失效,并总结如何正确的使用事务。

注:本篇验证的事务机制为使用@Transactional注解的声明式事务。

2.事务与事务传播

在做验证之前,需要对Spring中两个容易混淆的概念:事务和事务传播。

  • 事务:一种保持数据一致性的机制,具有ACID的特征。
  • 事务传播:Spring对不同方法的声明式事务组合起来使用,根据组合方式的不同,得到不一样的结果。

2.1 声明式事务说明

下面会简单的说明一下Spring中声明式事务的实现原理,不过在这之前先了解一下编程式事务,这是声明式事务的基础,下面是一个简化的Demo代码:

@Autowired
private PlatformTransactionManager transactionManager;

public void performSomeBusinessLogic() {
    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    
    try {
        // 执行业务逻辑
        
        // 事务提交
        transactionManager.commit(status);
    } catch (Exception ex) {
    	// 事务回滚
        transactionManager.rollback(status); 
        throw ex;
    }
}

我们可以使用PlatformTransactionManagerTransactionDefinitionTransactionStatus 这3个API完成编程式事务。


对于声明式事务来说,我们通过给方法打上一个@Transactional注解,就可以让这个方法以事务的方式来运行,而这种方式在Spring中往往是通过AOP来实现的,将@Transactional注解作为切点,进入一个处理事务的切面,这个切面里面要做的事就是开启事务,执行业务逻辑,判断事务是提交还是回滚。可以简单的理解为编程式事务那一套代码放到了通过AOP生成代理对象中了

2.2.声明式事务失效原因

既然是代理对象实现的,那就可以得出一个结论:如果AOP失效,那么声明式事务也会失效,于是我们可以发现事务失效的几个原因:

  1. 被声明的方法是非public方法、final方法、静态方法
  2. 对象内部自调用

对第2点,假设当前对象中有两个方法分别是a和ba上面没有写@Transactionalb上面写了,此时在a方法中使用this.b()来发起调用,这种情况下调用的是b的实例方法,并不是代理对象中的方法,没有代理自然事务就失效了,这也是下面会提到的事务传播失效的原因。

如果不想把两个方法放到两个不同的类中,可以在a方法中通过applicationContext.getBean(this.getClass());再获取一次代理对象,这样就不会失效了。


除此之外,再看一下上面的代码,事务会在catch块中调用rollback方法进行回滚,如果在业务流程中处理了异常没有向上抛出到代理对象中,就不会触发rollback,导致事务的回滚失效,因此我们可以得出事务失效的另一个原因:

  1. 异常被业务代码逻辑捕获,没有向上抛出到代理对象中

针对这个失效原因,需要补充一点的是,在某些场景下我们可能会通过切面对Service层通过AOP做统一异常处理,这种情况也可能会导致异常没有抛出到事务相关的代理对象中,导致回滚失效,这种情况隐藏的比较深,可能不容易排查到。


除了AOP层面导致的事务失效以外,我们再把视线集中的@Transactional本身上面,在这个注解有两个重要的配置,事务传播配置(Propagation)回滚异常配置(rollbackFor),这两个配置错误也可能导致事务失效。

  1. 配置了不支持事务的事务传播类型
  2. 需要回滚的异常类型未正确配置

对与第4点,下面的内容还会详细描述,这里说明一下回滚异常配置。
Transactional这个注解类中,对于rollbackFor有这么一段注释:
在这里插入图片描述
意思是说,未显式的指定回滚异常时,只要在抛出的异常为RuntimeException或者抛出的是Error时才会回滚,而其他的受检异常是不会回滚的。


最后,就是数据库本身的问题:

  1. 数据库引擎不支持事务(废话)

2.3.事务的传播机制

事务传播是多个带有事务的方法在组合使用时,希望通过配置不同的传播机制来达到不同的结果,Spring中定义的事务传播的配置一共有7种,分别是:

  • REQUIRED:多个方法在同一个事务下运行,其中有任一报错,都会一起回滚。
  • REQUIRED_NEW:调用时会新开一个事务,则多个方法在不同事务中运行,各自的方法报错只会回滚自己,互相不影响。
  • SUPPORT:被调用的方法会加入调用者的事务,如果调用者没有事务,则非事务运行。
  • NOT_SUPPORTED:如果调用者有事务,则暂停该事务,被调用方法以非事务运行。
  • MANDATORY:强制必须有事务,如果没有则抛出异常。
  • NESTED:多个方法属于同一个事务,但被调用方法的事务属于调用者的子事务。
  • NEVER:强制必须没有事务,如果有则抛出异常。

这里主要说一下NESTED,这是一种嵌套事务,与REQUIRED不同的是,被调用方法报错会将事务回滚到一个savePoint,不会引起调用者事务回滚,与REQUIRED_NEW不同的是,如果调用者出现了异常,也会带着被调用者一起回滚。

另外,在项目中使用事务传播机制,还得考虑一下数据库的隔离等级,两者配合很可能会给你带来死锁大礼包,举个简单的例子:用REQUIRED_NEW做事务传播,调用者和被调用者都update同一行数据使用id作为条件,调用者再等待被调用者执行完毕,被调用者再等待调用者完成事务,互相等待导致死锁。

2.4.事务传播失效原因

其实在上面事务失效中已经提到了,事务失效肯定传播也会失效,但这里需要强调的是。最容易忽略的原因还是因为使用的对象的自调用。
我们在编写项目的时候,很多情况下两个方法就是放在同一个service对象中的,不经意间就通过this.xxx进行调用了,然后一看就发现事务传播失效了。

3.事务使用建议

在条件允许的情况下,通过编程式事务来替换声明式事务可以有效的避免事务失效,并且可以更细粒度的控制事务,但缺点也比较明显,在业务逻辑中侵入了太多非业务逻辑的代码。

如果一定要使用声明式事务,根据上面提到的一些原因总结一下正确使用事务的几条建议:

  1. 使用public权限来定义方法,不要static,不要final
  2. 异常一定要向上抛出,即使逻辑内部要做异常处理,也需要抛出一个自定义异常
  3. 显式的配置Transactional中的事务传播和回调异常,不要使用默认配置
  4. 只要涉及到多个事务方法的调用,要注意自调用问题,一定要使用代理对象来调用

4.总结

本篇主要是描述了Spring的事务实践中导致事务失效的问题以及正确使用事务的建议,最后将事务失败的原因罗列在这里,方便后续查阅:

  1. 被声明的方法是非public方法、final方法、静态方法
  2. 对象内部自调用
  3. 异常被业务代码逻辑捕获,没有向上抛出到代理对象中
  4. 配置了不支持事务的事务传播类型
  5. 需要回滚的异常类型未正确配置
  6. 数据库引擎不支持事务(废话)

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

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

相关文章

【c语言】通讯录【动态版本:有排序和文件操作】

目录 一、通讯录定义 二、通讯录的实现 1、test.c中菜单的实现 2、通讯录的创建逻辑 3、初始化 4、检查容量和添加 5、查找 6、删除功能 7、修改功能 8、打印 9、查找并打印 10、qsort排序 11、摧毁 12、保存数据到文件 13、从文件中读数据 完整代码: 一、通讯录定…

Windows上安装 Go 环境

一、下载go环境 下载go环境:Go下载官网链接找到自己想下载的版本,点击下载,比如我这是windows64位的,我就直接点击最新的。 二、安装go环境 双击下载的.msi文件 next next 他默认的是c盘,你自己可以改,然…

Android 导入ncnn-android-yolov8-seg : 实现人体识别和人像分割

1. 前言 上篇文章我们在Android中使用OpenCV实现了人脸识别,这篇文章我们使用OpenCVYOLOv8NCNN 来实现人像分割的功能。 首先来看下效果,这里会识别出人体,并会用蓝色的框框出来,并会有标签标注识别出的物体是什么,概…

Python爬虫实战案例——第六例

文章中所有内容仅供学习交流使用,不用于其他任何目的!严禁将文中内容用于任何商业与非法用途,由此产生的一切后果与作者无关。若有侵权,请联系删除。 目标:去哪儿网指定城市人气值最高的15个景点评论数据采集 地址&a…

趣解设计模式之《小王的披萨店》

〇、小故事 小王看到最近越来越多的人喜欢吃披萨了,所以,他决定自己也开一个披萨店。最初开的时候,他只提供了一种口味的披萨,因为这样先试试水,看看生意如何,如果生意不好,也可以快速止损。 没…

一道签到题目 签到.zip

一道签到题目 https://www.xuenixiang.com/ctfexercise-competition-589.html 下载附件:签到.zip双击打开zip包。 进行base64转换 在线 Unicode 编码转换 | 菜鸟工具 (runoob.com) 获得压缩包密码:haishi 文字倒序工具,在线文字倒序 (qqxiuzi.cn)

一招根治Windows自带杀毒软件 Microsoft Defender

以毒攻毒:用腾讯电脑管家的文件粉碎机将Microsoft Defender 文件粉碎,再卸载腾讯电脑管家。 整个世界都安静了。 开机任务管理器就能看到 Microsoft Defender 又瞎忙起来了 打开文件位置: 记录下此时该文件的路径(保存在记事本里…

使用SPY++查看窗口信息去排查客户端UI软件问题

目录 1、使用SPY查看窗口的信息 2、使用SPY查看某些软件UI窗口用什么UI组件实现的 2.1、查看海康视频监控客户端安装包程序 2.2、查看华为协同办公软件WeLink 2.3、查看字节协同办公软件飞书 2.4、查看最新版本的Chrome浏览器 2.5、查看小鱼易连视频会议客户端软件 2.6…

cad图纸如何防止盗图(一个的制造设计型企业如何保护设计图纸文件)

在现代企业中,设计图纸是公司的重要知识产权,关系到公司的核心竞争力。然而,随着技术的发展,员工获取和传播设计图纸的途径越来越多样化,如何有效地防止员工复制设计图纸成为了企业管理的一大挑战。本文将从技术、管理…

如何用WiFi实现无线定位

一、WiFi主从模块设置 1. 实验器材 2. 实验步骤 ① 给控制板刷一套空的程序。 ② 将Esp8266模块连接到Bigfish扩展板上,并将扩展板插到控制板上。 ③ 在arduino的Seiral Monitor中,输入AT指令集,观察模块的相应应答。 3. 常用指令 ① 基础A…

使用不同尺寸的传感器拍照时,怎么保证拍出同样视场范围的照片?

1、问题背景 使用竞品机做图像效果对比时,我们通常都会要求拍摄的照片要视场范围一致,这样才具有可比性。之前我会考虑用同样焦距、同样分辨率的设备去拍照对比就可以了,觉得相机的视场范围只由镜头焦距来决定。 但如果对于不同尺寸的传感器…

5、Docker安装mysql主从复制与redis集群

安装mysql主从复制 主从搭建步骤 1.1 新建主服务器容器实例3307 docker run -p 3307:3306 --name mysql-master #3307映射到3306,容器名为mysql-master -v /app/mysql/mydata/mysql-master/log:/var/log/mysql #容器数据卷 -v /app/mysql/mydata/mysql-master/dat…

insightface实战:画出嘴巴和眼睛的mask

今天的目标是将人脸的嘴巴和眼睛区域抠出来,使用insightface简单实现出来,为了方便批量使用多进程跑数据,使用多进程的方式,下面是代码: import os import cv2 from multiprocessing import Pool import numpy as n…

五、接口测试工具:Postman

Postman是一款接口调试工具,是一款免费的可视化软件,同时支持各种操作系统平台,是测试接口的首选工具。 官网下载: https://www.postman.com/downloads/ 工作面板 简易的get请求 简易的post请求 案例:请求百度地图…

跨类型文本文件,反序列化与类型转换的思考

文章目录 应用场景序列化 - 对象替换原内容,方便使用编写程序取得结果数组 序列化 - JSON 应用场景 在编写热更新的时候,我发现了一个古早的 ini 文件,记录了许多有用的数据 由于使用的语言年份较新,没有办法较好地对 ini 文件的…

聊聊KISS(Keep It Simple, Stupid)原则

文章目录 1. 前言2. KISS原则的几项描述3. KISS原则和奥卡姆剃刀原则区别 1. 前言 KISS原则,是Keep It Simple, Stupid的缩写,翻译成中文就是“保持简单,愚蠢的人也能懂”。这是一种鼓励简单设计的设计原则。 KISS原则的主要思想是&#x…

Unity实现设计模式——中介者模式

Unity实现设计模式——中介者模式 用一个中介者对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使其松散耦合,而且可以独立地改变它们之间的交互。 这里使用一个生活中的例子来介绍中介者模式,比如当我们在…

【CTFHUB】SSRF原理之简单运用(一)

一、漏洞原理 SSRF 服务端请求伪造 原理:在某些网站中提供了从其他服务器获取数据的功能,攻击者能通过构造恶意的URL参数,恶意利用后可作为代理攻击远程或本地的服务器。 二、SSRF的利用 1.对目标外网、内网进行端口扫描。 2.攻击内网或本…

【开发篇】十二、缓存框架JetCache

文章目录 0、介绍1、JetCache远程缓存2、JetCache本地缓存3、标准配置文件4、JetCache方法缓存注解--Cached5、Cached4、CacheUpdate5、CacheInvalidate6、CacheRefresh7、缓存统计报告 上篇完成了Spring Cache底层技术的各种切换,但各个技术有各自的优缺点&#xf…

QT窗口的设置、按钮的创建和对象树的概念

目录 设置窗口属性 按钮的创建 对象树 对象树的概念 构建和析构的顺序问题 设置窗口属性 在Qt官方手册中查找QWidget相关信息 或者在QT软件帮助一栏直接搜索QWidget 即可找到一些要寻找的设置属性的函数 将代码写在构造函数中 widget.cpp #include "widget.h"…