【 Spring 事务 】

news2024/9/23 19:17:52

文章目录

  • 一、为什么需要事务(简单回顾)
  • 二、MySQL 中的事务使⽤
  • 三、Spring 中事务的实现
    • 3.1 Spring 编程式事务(手动事务)
    • 3.2 Spring 声明式事务(自动事务)
      • 3.2.1 @Transactional 作⽤范围
      • 3.2.2 @Transactional 参数说明
      • 3.2.3 @Transactional 不进行事务回滚的情况
      • 3.2.4 @Transactional ⼯作原理
  • 四、Spring事务隔离级别

一、为什么需要事务(简单回顾)

我们知道事务的简单定义是将⼀组操作封装成⼀个不可分割的执⾏单元(封装到⼀起),要么全部成功,要么全部失败(通过回滚)

举个例子:⽐如转账分为两个操作,第⼀步操作:A 账户 -100 元,第⼆步操作:B 账户 +100 元

如果没有事务,第⼀步执⾏成功了,第⼆步执⾏失败了,那么 A 账户的 100 元就平⽩⽆故消失了。⽽如果使⽤事务就可以解决这个问题,让这⼀组操作要么⼀起成功,要么⼀起失败

二、MySQL 中的事务使⽤

事务在 MySQL 有 3 个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:

--开启事务
start transaction;

-- 业务执⾏

-- 提交事务
commit;
-- 或回滚事务
rollback;

三、Spring 中事务的实现

3.1 Spring 编程式事务(手动事务)

Spring ⼿动操作事务(编程式事务)和上⾯ MySQL 操作事务类似,它也是有 3 个重要操作步骤:
开启事务(获取事务),提交事务或回滚事务。

具体如何在 spring 中实现呢?SpringBoot 内置了两个对象,DataSourceTransactionManager ⽤来获取事务(开启事务)、提交或回滚事务的,⽽ TransactionDefinition 是事务的属性,在获取事务的时候需要将TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus,实现用户添加,代码如下:

  1. 实现 mapper 接口
@Mapper
public interface UserInfoMapper {
    int add(UserInfo userInfo);
}
  1. xml 文件实现
<insert id="add">
       insert into userinfo(username,password) values (#{username},#{password})
</insert>
  1. service 代码实现
@Service
public class UserService {
    @Resource
    private UserInfoMapper userInfoMapper;
    
    public int add(UserInfo userInfo){
        return userInfoMapper.add(userInfo);
    }
}
  1. controller 中的代码实现(编程式业务实现)
@RestController
public class UserController {
    @Resource
    private UserService userService;

    @Resource
    private LogService logService;

    //通过注入的方式,而不是 new 对象
    @Resource
    //事务管理器,负责管理事务的行为,比如开始,提交,回滚等操作
    private DataSourceTransactionManager transactionManager;
    @Resource
    //是对事务属性的相关定义,比如设置事务的隔离传播机制,超时时间等
    private TransactionDefinition transactionDefinition;

    // 在此方法中使用编程式的事务
    @RequestMapping("/add")
    public int add(UserInfo userInfo) {
        // 非空效验【验证用户名和密码不为空】
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        // 开启事务(获取事务)
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);
                
        //执行业务代码        
        int result = userService.add(userInfo);
        System.out.println("受影响的行数:" + result);
        
        //transactionManager.commit(transactionStatus);   // 提交事务
        //transactionManager.rollback(transactionStatus); // 回滚事务
        return result;
    }
}    

4.1 获取到事务后,我们先验证提交事务后的效果 ! 通过 url 访问,再观察数据库的结果,如下

执行之前的数据表
在这里插入图片描述

通过 url 访问,得到受影响的行数为1
在这里插入图片描述

再次查看数据表
在这里插入图片描述

由此可见,数据添加成功 !!

4.2 再来验证事务回滚的效果 !!将事务提交的代码注释掉,添加事务回滚代码。紧接上面的数据表来验证

通过 url 访问,得到受影响的行数为1
在这里插入图片描述

查看数据表
在这里插入图片描述

我们发现,开起事务回滚后,数据表中并没有我们添加的那组数组,由此可见事务回滚成功了

扩展知识点:

如果我仅仅只是开启了事务,而不进行提交事务或回滚事务,这时我的操作也会达到回滚的效果。因为当你没有使用 commit 提交事务时,系统会认为没有提交事务,业务操作会自动回滚,因此数据不会持久化,这是一种保护机制,避免误提交!!


3.2 Spring 声明式事务(自动事务)

声明式事务的实现很简单,只需要在需要的⽅法上添加 @Transactional 注解就可以实现了,⽆需⼿动开启事务和提交事务,进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动回滚事务,具体实现代码如下:

    //注意:修饰方法时,该注解只能加到 Public修饰的方法上。修饰类时:表明该注解对该类中所有的 public ⽅法都⽣效
    @Transactional
    @RequestMapping("/add1")
    public int add1(UserInfo userInfo) {
        // 非空效验【验证用户名和密码不为空】
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        int result = userService.add(userInfo);
        System.out.println("受影响的行数:" + result);
        //int num = 10/0;
        return result;
    }

对比编程式事务,我们并没有繁琐的步骤,只添加了一个 @Transaction 注解,下面我们看效果

在这里插入图片描述

添加事务之前的数据表
在这里插入图片描述

添加事务之后的数据表
在这里插入图片描述

显而易见,我们添加数据成功了 !!


当我们的方法中出现了一个异常,我们再次验证事务是否会自动回滚呢? 将上面代码中的
int num = 10/0 异常解开注释

在这里插入图片描述

再次查看数据表
在这里插入图片描述

我们发现表中并没有我们添加的数据 !! 由此可见事务发生了回滚,添加操作失败了


3.2.1 @Transactional 作⽤范围

@Transactional 可以⽤来修饰⽅法或类:

  1. 修饰⽅法时:需要注意只能应⽤到 public ⽅法上,否则不⽣效
  2. 修饰类时:表明该注解对该类中所有的 public ⽅法都⽣效

3.2.2 @Transactional 参数说明

在这里插入图片描述

3.2.3 @Transactional 不进行事务回滚的情况

前面我们知道,当方法中存在异常时,我们的 @Transaction 会自动进行事务回滚 !但是当我们把这个异常捕获后即进行try…catch 处理,回滚事务就会失效,如下:

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    @Transactional
    @RequestMapping("/add2")
    public int add2(UserInfo userInfo) {
        // 非空效验【验证用户名和密码不为空】
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        int result = userService.add(userInfo);
        System.out.println("受影响的行数:" + result);
        try {
            int num = 10 / 0;
        } catch (Exception e) {
            // throw e;
            // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

解决方案:

  1. 将异常重新抛出去 throw e; (不推荐)
  2. 手动回滚事务 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

3.2.4 @Transactional ⼯作原理

1.@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。若⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,若继承了目标对象,会使⽤ CGLIB 动态代理。

2.@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务。

3.@Transactional 实现思路预览:
在这里插入图片描述

4.@Transactional 具体执⾏细节如下图所示:
在这里插入图片描述


四、Spring事务隔离级别

4.1 事务特性回顾:

事务有4 ⼤特性(ACID),原⼦性、持久性、⼀致性和隔离性,具体概念如下:

  1. 原⼦性:⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样
  2. ⼀致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯作
  3. 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
  4. 隔离性:数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化(Serializable)

⽽这 4 种特性中,只有隔离性(隔离级别)是可以设置的,那为什么要设置事务的隔离级别?

设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操作者预期的,为了防⽌其他的事务影响当前事务执⾏的⼀种策略

4.2 回顾MySQL 事务隔离级别:

  1. READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读
  2. READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读
  3. REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致。但也会有新的问题,⽐如此级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读(Phantom Read)
  4. SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多

在这里插入图片描述

● 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
● 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了(侧重点为 修改)。
● 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据(侧重点为 添加和删除)


在数据库中如何查询全局事务隔离级别和当前连接的事务隔离级别 !!

select @@global.tx_isolation,@@tx_isolation;

以上 SQL 的执⾏结果如下:
在这里插入图片描述


4.3 Spring 事务隔离级别:

相⽐于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个 Isolation.DEFAULT以所连接数据库的全局事务隔离级别为主

在这里插入图片描述

4.4 Spring 中设置事务隔离级别:

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置,具体操作如下图所示:

在这里插入图片描述


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

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

相关文章

Android 使用Retrofit+协程实现超简单大文件下载并回显进度条

安卓自带的进度条弹窗过时了&#xff0c;这里简单创建一个进度条弹窗 在drawable文件夹创建progress_dialog_bg_style.xml一个圆角白色背景样式 <?xml version"1.0" encoding"utf-8"?> <shape xmlns:android"http://schemas.android.com…

IPTV系统架构的分析与研究

1 引言   IPTV业务是伴随着宽带互联网的飞速发展而兴起的一项新兴的互联网增值业务,它利用宽带互联网的基础设施&#xff0c;以家用电视机和电脑作为主要终端 &#xff0c;利用网络机顶盒(STB,Set -TopBox) &#xff0c;通过互联网协议来传送电视信号.提供包括 电视节 目在 内…

嵌入式51单片机05-中断与定时器系列

文章目录 中断与定时器一、中断系统与定时器1. 中断简单介绍2. 定时器简单介绍 二、中断系列代码1. 中断操作&#xff08;中断控制LED灯亮灭&#xff09;&#xff08;1&#xff09;仿真电路图&#xff08;2&#xff09;源代码&#xff08;3&#xff09;实验结果 2. 中断操作&am…

c++强制类型转换:

强制类型转换&#xff1a;1. const属性用const_cast。 案例&#xff1a; 说明&#xff1a;该变量可以将变量的const 的属性去掉。如该案例&#xff0c;转换后修改x的值是合法的。2. 基本类型转换用static_cast。 案例&#xff1a; 说明&#xff1a;一般用在(1)基本类型&#xf…

新黑马头条项目经验(黑马)

swagger (1)简介 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(API Documentation & Design Tools for Teams | Swagger)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0c;有利于团队协作 接…

微服务知识

Spring Cloud Netfix&#xff1a;Eureka&#xff0c;Ribbon&#xff0c;Feign&#xff0c;Hystrix&#xff0c;Zuul | Gateway&#xff0c;Config Spring Colud Alibaba&#xff1a;Nacos&#xff0c;Sentinel&#xff0c;Seata Nacos通过Ribbon实现负载均衡&#xff0c;Ribb…

【java笔记】java多线程

目录 一、概念 1.1 什么是进程&#xff1f; 1.2 什么是线程&#xff1f; 1.3 什么事多线程&#xff1f; 1.4 进程和线程的关系 二、线程对象的生命周期 三、实现线程有两种方式 3.1 继承 java.lang.Thread&#xff0c;重写 run方法 3.2 实现 java.lang.Runnable 接口…

材料写作素材:关于“大”排比句40例

1.一轮思想政治“大督查”&#xff0c;一轮政策落实“大检查”&#xff0c;一次非公企业“大走访”&#xff0c;一次问题线索“大起底”&#xff0c;一批典型案例“大曝光”。 2.在重大风险挑战面前豁得出去、顶得上去&#xff0c;在重大困难考验面前迎难而上、敢于胜利&#…

【计网】WebSocket协议

目录 一、背景 二、WebSocket握手过程 三、SpringBoot中使用WebSocket协议 1、服务器 2、客户端 一、背景 一般的web开发以请求响应为主即客户端发送一个请求&#xff0c;服务器返回一个响应&#xff0c;这就使得类似聊天等需求基于HTTP协议进行实现时比较消费资源&#xf…

大数据之Hadoop分布式文件系统HDFS

目录&#xff1a; 一、介绍二、HDFS 设计原理三、原理图形介绍四、HDFS 常用 shell 命令五、HDFS相关JavaAPI 一、介绍 HDFS &#xff08;Hadoop Distributed File System&#xff09;是 Hadoop 下的分布式文件系统&#xff0c;具有高容错、高吞吐量等特性&#xff0c;可以部署…

厨电新十年,不可逆的行业分化与老板电器的数字进化

“人生就像滚雪球&#xff0c;最重要之事是发现湿雪和长长的山坡。”股神巴菲特的这句名言&#xff0c;让坡是否长、雪是否厚成为人们评价一个行业、一家公司的标准之一。 家电行业&#xff0c;厨电曾是最后一块“坡长雪厚”之地&#xff0c;投资者也对相关企业给出了相当的热…

FIR半带滤波器

FIR半带滤波器 半带滤波器原理&#xff1a; CIC滤波器是一种适合于工作在高采样率条件下的滤波器。 半带滤波器是一种非常适合于2倍抽取的FIR滤波器。 半带滤波器可以使2倍抽取的每秒乘法次数比一般线性相位的FIR滤波器减少近1/2。 半带滤波器是一种实现数字下变频的高效数…

暗藏的比特币白皮书已删除 苹果其实与Web3“格格不入”?

据悉&#xff0c;Apple已从其最新的Mac OS Ventura beta中删除了比特币白皮书&#xff0c;虽然该公司从未对白皮书的存在提供任何官方解释&#xff0c;但许多人猜测这是对加密货币日益增长的重要性的认可。 4月上旬&#xff0c;科技专家Andy Baio偶然发现&#xff0c;自己的Mac…

浅述 国产仪器 1652AM任意波形发生器

1652AM任意波形发生器是一款多通道、多功能的任意波形发生器。它在兼顾了输出信号高质量的同时&#xff0c;实现了高通道密度。它可与其它通用或专用模块化测试仪器构成综合测试系统或平台&#xff0c;支持众多解决方案&#xff0c;包括量子计算机调控信号生成、大规模MIMO信号…

活动目录(Active Directory)安全审计

延迟响应变化的影响可能会使原本应该微不足道的颠簸滚雪球变成无法弥补的损害。这在 Windows Active Directory 环境中更为重要&#xff0c;因为这种延迟造成的损害可能会使组织损失数百万美元&#xff01;在这种情况下&#xff0c;需要一个警惕的警报系统&#xff0c;该系统可…

WPF学习

一、了解WPF的框架结构 &#xff08;第一小节随便看下就可以&#xff0c;简单练习就行&#xff09; 1、新建WPF项目 xmlns&#xff1a;XML的命名空间 Margin外边距&#xff1a;左上右下 HorizontalAlignment&#xff1a;水平位置 VerticalAlignment&#xff1a;垂直位置 2…

性能测试开始前的需求调研

之前的博客聊聊性能测试开始前的准备工作&#xff0c;聊了一些关于性能测试开始前要做的准备工作。这篇博客&#xff0c;来谈谈性能测试开始前的需求调研阶段&#xff0c;我们要做什么&#xff0c;关注那些Point。。。 一、基本信息 信息类型说明项目名称项目归属的业务线&am…

低代码应用开发平台,让数据管理更简单!

在数据管理越来越规范化的今天&#xff0c;低代码应用开发平台也获得了进步和发展的机会和空间。想要将企业内部的数据资源做好系统管理&#xff0c;从而为各个时期的经营决策提供强有力的证据和基础&#xff0c;低代码应用开发平台就是其中备受喜爱的工具。本文主要从它的特点…

重磅!阿里云云原生合作伙伴计划全新升级:加码核心权益,与伙伴共赢新未来

在今天的 2023 阿里云合作伙伴大会上&#xff0c;阿里云智能云原生应用平台运营&生态业务负责人王荣刚宣布&#xff1a; “阿里云云原生合作伙伴计划”全新升级。他表示&#xff1a; 云原生致力于帮助企业客户最大限度的减轻运维工作&#xff0c;更好的实现敏捷创新&#x…

MQTT 协议

MQTT 简介 MQTT是一种基于客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范&#xff0c;易于实现。这些特点得它对很多场景来说都是很有的选择&#xff0c;特别是对于受限的环境如机器与机器的通信&#xff08;M2M&#xff09;以及物联网…