SpringBoot事务处理

news2024/11/25 4:58:43

一、事务回顾

回顾地址:  深入理解数据库事务(超详细)_数据库事务操作_Maiko Star的博客-CSDN博客

 

事务: 是一组操作的集合,是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败 

事务的操作

  • 开启事务(一组操作开始前,开启事务):start transaction / begin
  • 提交事务:(这组操作全部成功后,提交事务),commit
  • 回滚事务:(中间任何一个操作出现异常,回滚事务),rollback

SpringBoot中的事务是指一组操作数据库的动作集合,事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做。

二、SpringBoot事务的实现方式

(1)声明式事务管理(推荐)

        声明式事务管理建立在 AOP 之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行目标方法之后根据执行的情况提交或者回滚事务。声明式事务管理不需要入侵代码,通过 @Transactional 就可以进行事务的操作,推荐使用。

  • 在方法上添加@Transactional注解,表示该方法需要被事务管理。
  • 使用Propagation属性指定事务的传播行为,默认是REQUIRED,即如果当前存在事务,则加入该事务,如果没有事务,则新建一个事务。
  • 使用Isolation属性指定事务的隔离级别,默认是数据库的默认隔离级别。
  • 使用readOnly属性指定事务是否为只读,如果为只读,则在事务中不允许进行写操作,默认为false
  • 使用rollbackFor属性指定哪些异常触发事务回滚,默认为RuntimeException

示例代码:

@Service
@Transactional
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public User createUser(User user) {
        // 业务逻辑
        return userRepository.save(user);
    }

    public void updateUser(User user) {
        // 业务逻辑
        userRepository.save(user);
    }
}

(2)编程式事务管理

        编程式事务管理使用 TransactionTemplate或者直接使用底层的 PlatformTransactionManager。对于编程式事务管理,spring 推荐使用 TransactionTemplate。

  • 使用TransactionTemplate手动控制事务的开始和提交/回滚。
  • execute方法内部执行需要被事务管理的业务逻辑。

示例代码:

@Service
public class UserService {
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserRepository userRepository;

    public User createUser(User user) {
        return transactionTemplate.execute(status -> {
            try {
                // 业务逻辑
                return userRepository.save(user);
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }

    public void updateUser(User user) {
        transactionTemplate.execute(status -> {
            try {
                // 业务逻辑
                userRepository.save(user);
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
            return null;
        });
    }
}

三、@Transactional 注解介绍及使用

3.1 @Transactional 作用范围

@Fransactional 可以用来修饰方法或类:

  • 修饰方法时:表示当前的方法开启事务,需要注意只能应用在 public 方法上。
  • 修饰类时:表示当前该类下面的所有被 public 修饰的方法都开启事务。
  • 默认会回滚运RuntimeException 及其子类

3.2 @Transactional 参数说明

参数作用
value当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。
transactionManager当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。
propagation事务的传播行为,默认值为 Propagation.REQUIRED。
isolation事务的隔离级别,默认值为 Isolation.DEFAULT。
timeout事务的超时时间,默认值为-1(表示没有超时时间)。如果超过该超时时间显示但事务还没有完成,则自动回滚事务。
readOnly指定事务是否为只读事务,默认值为 false。为了忽略那些不需要事务的方法,比如读取数据,可以设置为 true。
rollbackFor用于指定能够被触发事务回滚的异常类型,可以指定多个异常类型。
rollbackForClassName用于指定能够被触发事务回滚的异常类型,可以指定多个异常类型。
noRollbackFor抛出异常的类型,不回滚事务,也可以指定多个异常类型。
noRollbackForClassName抛出异常的类型,不回滚事务,也可以指定多个异常类型。

参数名称功能描述readOnly该属性用于设置当前事务是否为只读模式,设置为 true 表示只读,false 表示可读可写,默认情况下是 false。

例如: @Transactional(readOnly = true)rollbackFor该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。

例如:

指定单一异常类:@Transactional(rollbackFor = Exception.class)

指定多个异常类:@Transactional(rollbackFor ={RuntimeException.class,Exception.class})rollbackForClassName该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。

例如:

指定单一异常类名:@Transactional(rollbackForClassName = "RuntimeException")

指定多个异常类名:@Transactional(rollbackForClassName = {"RuntimeException","Exception"})noRollbackFor该属性用于设置不需要进行回滚的异常类数组。使用方法同 rollbackFornoRollbackForClassName该属性用于设置不需要进行回滚的异常类名称数组。使用方法同 rollbackForClassNamepropagation该属性用于设置事务的传播行为。isolation该属性用于设置事务的隔离级别。timeout该属性用于设置事务的超时秒数,默认值为 -1 表示永不超时。

3.3 @Transactional 出现异常注意事项

        1.@Transactional 只能放在 public 修饰的方法上。

        2.@Transactional,不加任何参数时,默认会回滚运RuntimeException 及其子类,其它范围之外的异常 Spring 不会帮我们去回滚数据。

        3.@Transactional(rollbackFor = Exception.class),如果加上rollbackFor 参数,会回滚所指定的异常类,前提下一定要在catch中抛出相关异常类,否则事务还是失效的。

        4.@Transactional 在异常被捕获的情况下,不会进行事务的自动回滚

 默认情况下,Spring 中的事务如果遇到运行时异常,事务是会进行回滚的,但遇到非运行时异常,事务不会自动回滚。可以设置 rollbackFor 来解决非运行时异常不会被回滚的问题。示例代码如下:

@RequestMapping("/test3")
@Transactional
public String test3(@RequestParam String username, @RequestParam String pwd) {
    // 插⼊数据库
    int result = userService.addUser(username, pwd);
    try {
        // 执⾏了异常代码
        int i = 10 / 0;
   } catch (Exception e) {

        
    }

以上代码虽然出现了算数异常,但是由于主动捕获了,且没有进行抛出,因此不会进行事物的回滚,数据库中会插入该条数据。

如果要解决出现异常事务不能自动回滚的问题,以下提供两种解决方案:

(1)方案一:对于捕获的异常,事务是不会自动回滚的,因此可以在捕获异常后主动将该异常重新抛出。默认只回滚RunTimeException,故我们这里演示抛出RunTimeException,示例代码如下:

@RequestMapping("/test3")
@Transactional
public String test3(@RequestParam String username, @RequestParam String pwd) {
    // 插⼊数据库
    int result = userService.addUser(username, pwd);
    try {
        // 执⾏了异常代码
        int i = 10 / 0;
   } catch (Exception e) {
		// 将异常重新抛出
        throw new RunTimeException();
   }
    return "测试完成!"; 
}

方案二(推荐):手动去回滚事务,可以通过方法 TransactionAspectSupport.currentTransactionStatus() 得到当前的事务,然后设置回滚方法 setRollbackOnly 就可以实现回滚。示例代码如下:

@RequestMapping("/test3")
@Transactional
public String test3(@RequestParam String username, @RequestParam String pwd) {
    // 插⼊数据库
    int result = userService.addUser(username, pwd);
    try {
        // 执⾏了异常代码
        int i = 10 / 0;
    } catch (Exception e) {
        // 手动回滚
TransactionStatus transactionStatus = TransactionAspectSupport.currentTransactionStatus();
    transactionStatus.setRollbackOnly();
    }
    return "测试完成!";
}

使用了TransactionAspectSupport类来获取当前的事务状态,并通过调用setRollbackOnly()方法手动回滚事务。这是Spring框架中一种手动回滚事务的方式。

3.4  @Transactional 失效的情况排查

问题:方法上添加了 @Transactional 注解,为什么没有进行回滚?


排查一:是否使用了 try catch 进行了异常捕获,并且捕获之后,没有通过 throw new RuntimeException(); 进行异常抛出。因为对于 spring aop 异常捕获原理,被拦截的方法需要显示的抛出异常,并不能进行任何处理,这样 aop 代理才能捕获到方法的异常,才能进行事务的回滚操作;默认清空下,aop 只捕获 RuntimeException 的异常,但是可以通过配置来捕获特定的异常并回滚。


排查二:是否使用了 try catch 进行了异常信息捕获,如果是可以在 catch 语句中添加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),手动回滚事务

排查三:排查是不是产生了自调用的问题。在 spring 的 aop 代理下,只有目标方法被外部调用,目标方法才由 spring 生成的代理对象来管理;若统一类中的其他没有用 @Transactional 注解进行修饰的方法内部调用了 用 @Transactional 注解进行修饰的方法,有 @Transactional 注解的方法的事务将会被忽略,不发生回滚。(周氏概括:即没有使用@Transactional 注解修饰的方法调用了用 @Transactional 注解进行修饰的方法,将会导致事务失效)

备注:如果确实需要这样操作,只需要把 @Transactional 注解加在当前的类名上就可以了 或者使用 AspectJ 取代 spring aop 进行代理。


排查四:@Transactional 注解只被应用到 public 修饰的方法上;如果在 protected、private等修饰的方法上,@Transactional 注解不会报错,但是这个注解的将不会生效。


排查五:@Transactional 注解不会对当前修饰的方法的处理异常的子方法生效。比如:我们在方法 A 中声明了 @Transactional 注解,但是 A 方法的内部调用的 方法 B 和 方法 C,其中方法 B 进行了 数据库的操作,但是该部分的异常被方法 B 进行了处理并且没有进行 抛出,这样的话事务是不会生效的。如果想要事务生效,需要将子方法的事务控制交给调用的方法,在子方法中使用 @Transactional注解并通过 rollbackFor 指定定回滚的异常 或者直接将异常抛出。(周氏概括:使用@Transactional 注解修饰的A方法 调用了 没有用@Transactional 注解修饰的B方法,但是B方法使用try catch处理了异常,这将导致事务失效。使用@Transactional 注解修饰的A方法 调用了 没有用@Transactional 注解修饰的B方法,但是B方法将异常向上抛出或使用@Transactional注解并通过 rollbackFor 指定定回滚的异常,事务不会失效。)

备注:在使用事务的时候,最好把子方法的异常进行抛出,交给调用的方法进行处理。

四、SpringBoot事务传播机制(了解)

4.1 事务传播机制的定义

Spring 事务传播机制定义了多个包含了事务的方法在相互调用时,事务是如何在这些方法之间进行传递的。

4.2 事务传播机制的作用

  • 事务隔离级别是保证多个并发事务执行的可控性,而事务传播机制是保证一个事务在多个调用方法之间的可控性。
  • 事务隔离级别是解决多个事务同时调用数据库的问题,而事务传播机制是解决一个事务在多个节点中传递的问题。

4.3 Spring 事务传播机制有7种

  1. Propagation.REQUIRED: 默认的事务传播机制,它表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. Propagation.SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  3. Propagation.MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  4. Propagation.REQUIRES_NEW: 表示创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,该传播机制修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
  5. Propagation.NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  6. Propagation.NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
  7. Propagation.NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 Propagation.REQUIRED。

嵌套事务和加入事务的区别:

  • 嵌套事务:回事有问题的事务,但主事务不受影响。
  • 加入事务:如果任意一个方法出现异常,那么整个事务会回滚。

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

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

相关文章

什么是 dropblock

大家好啊,我是董董灿。 之前介绍过 dropout 算法,它在训练神经网络中,可以随机丢弃神经元,是一种防止网络过拟合的方法。 但是在卷积神经网络中,dropout 的表现却不是很好,于是研究人员又搞了一个“结构化…

五大自动化测试的 Python 框架

1、Selenium: Selenium 是一个广泛使用的自动化测试框架,用于测试Web应用程序。它支持多种浏览器,并通过模拟用户在浏览器中的操作来进行测试。Selenium 的 Python 客户端库是 Selenium WebDriver,它提供了一组API来编写测试脚本&#xff0c…

系列十八、Spring bean线程安全问题

一、概述 我们知道Spring中的bean,默认情况下是单例的,那么Spring中的bean是线程安全的吗?这个需要分情况考虑,bean中是否存在成员变量?bean中的成员变量是怎么处理的?...,针对bean的状态会有不…

【挑战业余一周拿证】一、亚马逊云科技简介 - 第 1 节 - 模块 1 简介

CSDN 官方中文视频(免费):点击进入 一、亚马逊云科技简介 第 1 节 - 模块 1 简介 1、讲师:李锦鸿 部门:亚马逊云科技培训与认证部门 方向:从事数据中心及云计算相关产品与解决方案工作 课程&#xff…

【Linux系统编程】冯 • 诺依曼体系结构(什么是冯 • 诺依曼体系结构?冯 • 诺依曼体系结构如何应用?)

目录 一、前言 二、什么是冯 • 诺依曼体系结构? 💦 冯 • 诺依曼体系结构的发展推导 💦冯 • 诺依曼体系结构的5大部件 ⭐输入和输出设备 ⭐存储器 ⭐中央处理器(CPU) 💦冯 • 诺依曼体系结构的细节…

2016年五一杯数学建模A题购房中的数学问题解题全过程文档及程序(采光与房款)

2016年五一杯数学建模 A题 购房中的数学问题 原题再现 随着现代社会经济的快速发展,房地产成为国家经济发展中重要的经济增长点之一。为了充分利用楼房建设的土地面积,开发商经常会选择建筑高层住宅。在购买住房时,影响消费者选择购房的因素…

设计模式—依赖倒置原则(DIP)

1.概念 依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。 通俗的讲&#xff1…

elastic -job和springboot集成实现分布式调度5

一 案例介绍说明 1.1 案例介绍 基于 Spring boot 集成方式的而产出的工程代码,完成对作业分片的实现,文件数据备份采取更接近真实项目的数 据库存取方式。 1.分片设置 2.每个线程获取给自的类型 二 操作说明 2.1 数据表的初始化 DROP TABLE IF EXIS…

【算法萌新闯力扣】:回文链表

力扣题目:回文链表 开篇 今天是备战蓝桥杯的第23天。我加入的编程导航算法通关村也在今天开营啦!那从现在起,我的算法题更新会按照算法村的给的路线更新,更加系统。大家也可以关注我新开的专栏“算法通关村”。里面会有更全面的知…

VMware安装部署kail镜像服务器【详细包含百度云盘镜像】

VMware安装部署kail镜像服务器【详细包含百度云盘镜像】 kail是一个很好玩的操作系统,不多说了哈 下载kail镜像 kail官网:https://www.kali.org/get-kali/#kali-platforms 百度云盘下载: 链接:https://pan.baidu.com/s/1PRjoP_1v8DEZ7-dA_…

python桌面开发PyQt6库和工具库QTDesigner安装和配置

一、安装PyQt6 二、安装pyqt6-tools 三、安装外部工具 四、创建QTDesigner 1.首先查找designer.exe的路径(可以自己在窗口中选择,也可以使用Everything搜索) 2.使用Everything搜索后会出现多个designer.exe,选中,OpenPath 3.选择…

讯飞星火知识库文档问答Web API的使用(二)

上一篇提到过星火spark大模型,现在有更新到3.0: 给ChuanhuChatGPT 配上讯飞星火spark大模型V2.0(一) 同时又看到有知识库问答的web api,于是就测试了一下。 下一篇是在ChuanhuChatGPT 中单独写一个基于星火知识库的内容…

【Web-Note】 JavaScript概述

JavaSript基本语法 JavaSript程序不能独立运行&#xff0c;必须依赖于HTML文件。 <script type "text/javascript" [src "外部文件"]> JS语句块; </script> script标记是成对标记。 type属性&#xff1a;说明脚本的类型。 "text/jav…

rdf-file:分布式环境下的文件处理

一&#xff1a;数据量大了以后&#xff0c;单机解析或者生成文件的效率就很低&#xff0c;需要通过集群处理 机构过来的文件&#xff1a;我们先对文件进行分片&#xff0c;在利用集群集群处理分片文件。给机构文件&#xff1a;分库分表数据&#xff0c;每个分表生成一个分片文…

什么是分布式锁?Redis实现分布式锁详解

目录 前言&#xff1a; 分布式系统买票示例 引入redis做分布式锁 引入过期时间 引入校验id 引入lua脚本 过期时间续约问题 redlock算法 小结&#xff1a; 前言&#xff1a; 在分布式系统中&#xff0c;涉及多个主机访问同一块资源&#xff0c;此时就需要锁来做互斥控制…

Linux篇:文件系统

一、共识原理&#xff1a; 文件文件内容文件属性 磁盘上存储文件存文件的内容&#xff08;数据块&#xff09;存文件的属性&#xff08;inode&#xff09; Linux的文件在磁盘中存储是将属性和内容分开存储的。 二、硬件简述&#xff1a; 1. 认识硬件 磁盘&#xff1a;唯一的一…

Vue+ElementUI技巧分享:周数选择器

文章目录 前言一、周数的计算逻辑1.1 周数的定义1.2 年初周数的确定1.3 周数的计算方法 二、代码实现2.1 计算周数2.2 获取周的日期范围2.3 根据周数获取日期范围2.4 控件引用2.4.1 控件引用代码分析2.4.2 初始化变量代码分析 2.5 周数选择器完整代码 总结 前言 Vue.js和Eleme…

文心一言 VS 讯飞星火 VS chatgpt (143)-- 算法导论12.1 3题

三、用go语言&#xff0c;设计一个执行中序遍历的非递归算法。&#xff08;提示:一种容易的方法是使用栈作为辅助数据结构;另一种较复杂但比较简洁的做法是不使用栈&#xff0c;但要假设能测试两个指针是否相等。&#xff09; 文心一言&#xff0c;代码正常运行&#xff1a; …

Linux 面试题(一)

目录 1、绝对路径用什么符号表示&#xff1f;当前目录、上层目录用什么表示&#xff1f;主目录用什么表示? 切换目录用什么命令&#xff1f; 2、怎么查看当前进程&#xff1f;怎么执行退出&#xff1f;怎么查看当前路径&#xff1f; 3、怎么清屏&#xff1f;怎么退出当前命…

4G模块(EC600N)通过MQTT连接华为云

目录 一、前言 二、EC600N模块使用 1&#xff0e;透传模式 2&#xff0e;非透传模式 3、华为云的MQTT使用教程&#xff1a; 三、具体连接步骤 1、初始化检测 2、打开MQTT客户端网络 3、创建产品 4、创建模型 5、注册设备 6、连接客户端到MQTT服务器 7、发布主题消…