Spring中的事务和事务的传播机制

news2024/11/17 9:41:23

事务是一组操作的集合,不可以被分割。事务会把所有的操作作为一个整体,这组操作要么全部成功,要么全部失败。

事务有三种操作:

  1. 开启事务;
  2. 提交事务;
  3. 回滚事务。

如果代码的执行逻辑是这样:

开启事务

业务A

回滚事务

此时A当中的所有操作都不会生效

开启事务

业务A

提交事务

开启事务后只有这种情况下A中的逻辑才生效

Spring中事务的实现有两种

编程式(手动操作事务)

Spring Boot对于事务操作内置了两个类,我们在使用时可以选择直接注入:

  • DataSourceTransactionManager:事务管理器,里面包含了事务的操作和获取;
  • TransactionDefinition:事务的属性。在获取事务时需要充当参数。

提交事务

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    //获取事务管理器
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    //获取事务属性
    @Autowired
    private TransactionDefinition definition;

    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/fun1")
    public void fun1() {
        //获取并开启事务
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(definition);
        //业务操作
        //向数据库中插入一条数据
        userMapper.userInsert("zhangsan","man");
        //打印日志
        log.info("数据插入完成");
        //提交事务
        dataSourceTransactionManager.commit(transaction);
    }
}
@Mapper
public interface UserMapper {
    @Insert("insert into userinfo(username,gender) values (#{userName},#{gender});")
    void userInsert(String userName, String gender);
}

这是数据库的初始状态

代码执行后数据成功插入

回滚事务

@RequestMapping("/fun1")
public void fun1() {
    //获取并开启事务
    TransactionStatus transaction = dataSourceTransactionManager.getTransaction(definition);
    //业务操作
    userMapper.userInsert("zhangsan","man");
    log.info("数据插入完成");
    //提交事务
//        dataSourceTransactionManager.commit(transaction);
    //回滚事务
    dataSourceTransactionManager.rollback(transaction);
}

当代码执行成功后,数据库中的数据并没有变多。

注解式(利用注解自动实现事务)

使用注解实现事务是非常简单的只需要给需要添加事务的方法加上@Transactional注解。添加该注解后程序会自动的在方法开始前开启事务,在方法结束后提交事务;如果在方法执行中发生了没有处理的异常会自动进行回滚事务

@Transactional既可以修饰方法也可以修饰类:

  1. 修饰方法时该方法必须是被public修饰的方法,否则既不会生效也不会报错;
  2. 当修饰类时,会对该类中的所有被public修饰的方法生效。

当方法正常执行完毕后会自动提交事务 :

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    @Transactional
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("lisi","man");
        log.info("数据插入完成");
    }
}

当方法执行过程中发生异常时自动回滚事务 (该注解默认只回滚运行时异常<RuntimeException
>和错误<error>):

发生运行时异常,事务回滚:

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    @Transactional
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("lisi111","man");
        log.info("数据插入完成");
        //发生运行时异常,事务回滚
        throw new RuntimeException();
    }
}

编译时异常不会回滚:

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    @Transactional
    @RequestMapping("/fun2")
    public void fun2() throws IOException {
        userMapper.userInsert("lisi111","man");
        log.info("数据插入完成");
        //发生编译时异常,事务不会回滚
        throw new IOException();
    }
}

此时尽管程序已经报错了,可数据还是正常插入了。如何解决这个问题呢?

rollbackFor

可以通过配置 @Transactional 注解当中的 rollbackFor 属性,通过 rollbackFor 这个属性来指定出现何种异常类型时事务进行回滚。

这个属性的类型是数组需要注意

Class<? extends Throwable>[] rollbackFor() default {};
@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {
    @Autowired
    private UserMapper userMapper;

    //此时会回滚所有的Exception类型的异常
    @Transactional(rollbackFor = {Exception.class})
    @RequestMapping("/fun2")
    public void fun2() throws IOException {
        userMapper.userInsert("lisi222","man");
        log.info("数据插入完成");
        //此时发生编译时异常,事务回滚
        throw new IOException();
    }
}

此时error类型的错误依然会进行回滚。

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {

    @Autowired
    private UserMapper userMapper;

    @Transactional(rollbackFor = {Exception.class})
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("lisi222","man");
        log.info("数据插入完成");
        //会发生栈溢出错误,仍然会回滚
        while(true) {
            fun2();
        }
    }
}

手动回滚事务

使用 TransactionAspectSupport.currentTransactionStatus() 得到当前的事务,并使用setRollbackOnly使事务进行回滚

@Slf4j
@RestController
@RequestMapping("/trans")
public class Test {

    @Autowired
    private UserMapper userMapper;

    @Transactional(rollbackFor = {Exception.class})
    @RequestMapping("/fun2")
    public void fun2() {
        userMapper.userInsert("66666","man");
        log.info("数据插入完成");
        //手动设置回滚事务
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

事务隔离级别

SQL中的事务隔离级别:

  1. 读未提交(READ UNCOMMITTED):读未提交,也叫未提交读。该隔离级别的事务可以看到其他事务中未提交的数据(未提交的数据可能会发生回滚,但是该隔离级别却可以读到,这个问题称之为脏读)
  2. 读已提交(READ COMMITTED):也叫提交读,该隔离级别的事务能读取到已经提交事务的数据。(该隔离级别不会有脏读的问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同SQL查询可能会得到不同的结果,这种现象叫做不可重复读)
  3. 可重复读(REPEATABLE READ):事务不会读到其他事务对已有数据的修改,即使其他事务已提交。可重复读,是MySQL的默认事务隔离级别。(虽然可以确保同一事务多次查询的结果一致,但是其他事务新插入的数据,是可以感知到A事务正在执行时,另⼀个事务成功的插入了某条数据,而此时A事务如果再查寻数据库就会导致两次查找到的“结果集”不同<数据变多了>,这个现象叫幻读。)
  4. 串行化(SERIALIZABLE):序列化,事务最高隔离级别。它会强制事务排序,使之不会发生冲突,从而解决了脏读,不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。

Spring中的事务隔离级别:

  1. Isolation.DEFAULT :以连接的数据库的事务隔离级别为主;
  2. Isolation.READ_UNCOMMITTED :读未提交;
  3. Isolation.READ_COMMITTED :读已提交;
  4. Isolation.REPEATABLE_READ :可重复读;
  5. Isolation.SERIALIZABLE :串行化;

事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置

@Transactional(isolation = Isolation.DEFAULT)
@RequestMapping("/fun2")
public void fun2() {}

事务的传播机制

如果A方法中调用B方法那么B方法是使用A方法的事务还是自己的事务,事务的传播机制就是为了解决该问题。

@Transactional 注解支持事务传播机制的设置,通过 propagation 属性来设置。

Spring 事务传播机制有 7 种(在A方法中调用B(七种传播机制都设置在该方法上)方法):

  1. Propagation.REQUIRED:默认的事务传播级别。如果A存在事务,则B加入该事务。如果A没有事务,则B创建一个新的事务;
  2. Propagation.SUPPORTS:如果A存在事务,则B加入该事务。如果A没有事务,则B以非事务的方式继续运行;
  3. Propagation.MANDATORY:强制性。如果A存在事务,则B加入该事务。如果A没有事务,B抛出异常;
  4. Propagation.REQUIRES_NEW:创建一个新的事务。不管A是否开启事务,B都会重新创建一个事务,且创建的事务相互独立,互不干扰;
  5. Propagation.NOT_SUPPORTED:以非事务方式运行。无论A是否存在事务,B都以非事务运行;
  6. Propagation.NEVER:以非事务方式运行。如果A存在事务,则抛出异常;
  7. Propagation.NESTED :如果A存在事务,则B创建一个事务作为当前事务的嵌套事务来运行。如果A没有事务,B创建一个事务。
@Transactional(propagation = Propagation.MANDATORY)
@RequestMapping("/fun2")
public void fun2() {}

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

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

相关文章

国际黄金价格是什么?和黄金价格有何区别?

黄金是世界上最珍贵的贵金属之一&#xff0c;其价值被无数人所垂涎。而国际黄金价格作为市场上的参考指标&#xff0c;直接影响着黄金交易的买卖。那么国际黄金价格到底是什么&#xff0c;与黄金价格又有何区别呢&#xff1f;本文将为您详细解答。 国际黄金价格是指以美元计量的…

【双碳】Acrel-1000DP分布式光伏并网及数据采集与控制的方式

摘要&#xff1a; 在“双碳”、整县分布式光伏等相关政策的目标背景下&#xff0c;分布式新能源广泛建设&#xff0c;对分布式新能 源规划的科学性提出更高的要求&#xff0c;有源配电网调度面临大规模分布式新能源参与后的运行管理问题&#xff0c;增 大了电网运行管理的风险和…

用友 NC 23处接口XML实体注入漏洞复现

0x01 产品简介 用友 NC 是用友网络科技股份有限公司开发的一款大型企业数字化平台。 0x02 漏洞概述 用友 NC 多处接口存在XML实体注入漏洞,未经身份验证攻击者可通过该漏洞读取系统重要文件(如数据库配置文件、系统配置文件)、数据库配置文件等等,导致网站处于极度不安全…

图搜索基础-深度优先搜索

图搜索基础-深度优先搜索 参考原理引入流程解析手推例子 代码实现运行结果结果分析 参考 理论参考&#xff1a;深蓝学院 实现参考&#xff1a;github项目 原理 引入 对于这样一个图&#xff0c;我们试图找到S到G的通路&#xff1a; 计算机程序不会像人眼一样&#xff0c;一…

事件驱动的奇迹:深入理解Netty中的EventLoop

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 事件驱动的奇迹&#xff1a;深入理解Netty中的EventLoop 前言基础概念EventLoop的工作原理Channel与EventLoop的关系定时任务与延时任务EventLoop的生命周期EventLoop中的线程模型性能优化与最佳实践 …

前后端依赖下载上传

在某些情况下&#xff0c;可能需要在没有互联网连接的环境中进行构建或部署。通过提前下载所有依赖&#xff0c;你可以将它们保存在本地&#xff0c;然后在没有网络连接时使用&#xff0c;提高构建或部署的效率。 前端下载依赖 脚本getTzgUrl.js const { readFileSync, writ…

前端架构: 脚手架之包管理工具的案例对比及workspace特性的基本使用

Npm WorkSpace 特性 1 &#xff09;使用或不使用包管理工具的对比 vue-cli 这个脚手架使用 Lerna 管理&#xff0c;它的项目显得非常清晰在 vue-cli 中包含很多 package 点开进去&#xff0c;每一个包都有package.json它里面有很多项目&#xff0c;再没有 Lerna 之前去维护和管…

sql | leecode 1147 |即时事务配送II | sql 优化

sql 有时候还是挺有意思的&#xff0c;有时候不只是crud 下面来看一道sql 题&#xff0c;第一个题解能跑完&#xff0c;但是超时 浮躁的人总是很难看的进去&#xff0c;有时候孤单又在所难免&#xff0c;所以啊 要继续坚持&#xff0c;原来中学老师讲的那句&#xff0c;要耐住寂…

arm服务器上部署kibana

1.首先需要从elasticsearch对应的kibana版本(arm) ​​​​​​Download Kibana Free | Get Started Now | Elastic 注意:选平台时切勿选错,linux aarch64,并选择elasticsearch对应的历史版本 2.可以通过rz命令上传压缩包至 linux 服务器进行解压&#xff0c;存放路径建议和e…

物联网通信协议介绍

为了方便&#xff0c;将物联网通信协议分为两大类&#xff0c;一类是接入协议&#xff0c;一类是通讯协议。接入协议一般负责子网内设备间的组网及通信&#xff1b;通讯协议主要是运行在传统互联网TCP/IP协议之上的设备通讯协议&#xff0c;负责设备通过互联网进行数据交换及通…

数据结构:排序算法+查找算法

一、概念 程序数据结构算法 1.算法的特性和要求 特性&#xff1a; 确定性&#xff08;每次运行相同的输入都是同样的结果&#xff09;、有穷性、输入、输出、可行性 设计要求&#xff1a; 正确性、高效率、低存储、健壮性、可读性 2.时间复杂度 3.常见排序算法的时间复杂…

K8S存储卷与PV,PVC

一、前言 Kubernetes&#xff08;K8s&#xff09;中的存储卷是用于在容器之间共享数据的一种机制。存储卷可以在多个Pod之间共享数据&#xff0c;并且可以保持数据的持久性&#xff0c;即使Pod被重新调度或者删除&#xff0c;数据也不会丢失。 Kubernetes支持多种类型的存储卷…

MyBatis 学习(二)之 第一个 MyBatis 案例

目录 1 配置 MyBatis 方式 1.1 XML 配置文件 1.2 Java 注解配置 1.3. Java API 配置 2 在 MySQL 中创建一张表 3 创建一个基于 Maven 的 JavaWeb 工程 4 编写 User 实体类 5 创建Mybatis全局配置文件 6 编写一个 DAO 或 Mapper 接口 7 编写 SQL 映射配置文件&#xf…

Vuepress的使用

介绍 将markdown静态资源转换成html。 动态资源的转换还有很多&#xff0c;为什么要使用Vuepress&#xff1f; 目录分析 项目配置 详情 具体配置请看文档 插件配置 vuepress-theme-vdoing 主题插件 npm install vuepress-theme-vdoing -D先安装依赖配置主题 使用vuep…

Android 架构MVI、MVVM、MVC、MVP

目录 一、MVC&#xff08;Model-View-Controller&#xff09; 二、 MVP&#xff08;Model-View-Presenter&#xff09; 三. MVVM&#xff08;Model-View-ViewModel&#xff09; 四. MVI&#xff08;Model-View-Intent&#xff09; 五.MVI简单实现 先简单了解一下MVC、MVP和…

5.WEB渗透测试-前置基础知识-常用的dos命令

内容参考于&#xff1a; 易锦网校会员专享课 上一篇内容&#xff1a;4.WEB渗透测试-前置基础知识-快速搭建渗透环境&#xff08;下&#xff09;-CSDN博客 常用的100个CMD指令 1.gpedit.msc—–组策略 2. sndrec32——-录音机 3. Nslookup——-IP地址侦测器 &#xff0c;是一个…

Keepalived 双机热备基础知识

7.1 Keepalived 双机热备基础知识 Keepalived起初是专门针对LVS设计的一款强大的辅助工具&#xff0c;主要用来提供故障切换(Failover) 和健康检查査(Health Checking)功能一一判断LVS 负载调度器、节点服务器的可用性&#xff0c;及时隔离并替 换为新的服务器&#xff0c;当故…

使用腾讯云go sdk 查询对象存储中最新文件

背景&#xff1a; 腾讯云账号下&#xff0c;有很多对象存储COS桶&#xff1a; 我现在想确认某一个对象存储桶的活跃程度&#xff0c;简单的来说。我想知道这个桶里面最后上传的一个文件是什么&#xff0c;以及它的上传时间戳。 本文将介绍如何使用腾讯云对象存储&#xff08;…

iMazing3安全吗?好不好用?值不值得下载

一、安全性 iMazing在设计和开发过程中&#xff0c;始终把用户数据的安全性放在首位。它采用了多种先进的安全技术来确保用户数据在传输、备份和存储过程中的安全。 iMazing3Mac-最新绿色安装包下载如下&#xff1a; https://wm.makeding.com/iclk/?zoneid49816 iMazing3Wi…

森林监测VR虚拟情景再现系统更便利

AI人工智能技术已经逐渐渗透到各个领域&#xff0c;为我们的生活带来了诸多便利。在虚拟仿真教学领域&#xff0c;AI技术的应用也日益丰富&#xff0c;为虚拟情景交互体验带来了前所未有的好处。 提高VR虚拟情景的逼真度 通过深度学习和计算机视觉等技术&#xff0c;AI/VR虚拟现…