一文学会Spring 实现事务,事务的隔离级别以及事务的传播机制

目录

一.Spring (Spring Boot) 实现事务

1.通过代码的方式手动实现事务 (手动档的车)

2.通过注解的方式实现声明式事务 (自动挡的车)

二.事务的4大特性(ACID)

三.事务的隔离级别

①Mysql的事务隔离级别:

②Spring的事务隔离级别:

四.事务的传播机制

①事务传播机制的概念

②Spring 事务传播机制包含以下 7 种:

1. Propagation.REQUIRED

2. Propagation.SUPPORTS

3. Propagation.MANDATORY (mandatory:强制性)

4. Propagation.REQUIRES_NEW

5. Propagation.NOT_SUPPORTED

6. Propagation.NEVER

7. Propagation.NESTED

③以情侣关系为例理解这七种传播机制


一.Spring (Spring Boot) 实现事务

1.通过代码的方式手动实现事务 (手动档的车)

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager; //事务数据管理器@Autowiredprivate TransactionDefinition transactionDefinition; //设置事务属性的对象@RequestMapping("/add")public int add(UserInfo userInfo) {// todo:非空效验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 1.开启事务TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition); //transactionStatus就是事务// 手动设置创建时间和修改时间的默认值userInfo.setCreatetime(LocalDateTime.now().toString());userInfo.setUpdatetime(LocalDateTime.now().toString());int result = userService.add(userInfo);System.out.println("添加: " + result);// 2.回滚事务
//        transactionManager.rollback(transactionStatus);transactionManager.commit(transactionStatus);return result;}
}
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public Integer add(UserInfo userInfo) {return userMapper.add(userInfo);}
}
@Mapper
public interface UserMapper {int add(UserInfo userInfo);
}

 UserMapper.xml中的添加操作

<mapper namespace="com.example.demo.mapper.UserMapper"><insert id="add">insert into userinfo(username,password,createtime,updatetime)values(#{username},#{password},#{createtime},#{updatetime})</insert>
</mapper>

运行前数据库中的数据:

运行程序提交事务:

 

此时的代码中是没有执行回滚事务的操作的,所以数据库当中能够看到新增的数据

如果开启回滚事务,关闭提交事务,则idea中能够看见操作的信息,但是数据库中找不到新增的数据

2.通过注解的方式实现声明式事务 (自动挡的车)

通过使用@Transactional注解来实现

@Transactional的特点:

Ⅰ.可以添加在类上或方法上

        该注解只在public修饰的方法上生效,在类上添加该注解则表明当前类的所有公共方法都会自动提交或回滚事务

Ⅱ.在方法执行前开启事务,在方法执行完(没有任何异常) 自动提交事务,但是如果方法在执行期间出现异常,那么将自动回滚事务

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager; //事务数据管理器@Autowiredprivate TransactionDefinition transactionDefinition; //设置事务属性的对象@Transactional // 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// todo:非空效验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result = userService.add(userInfo);System.out.println("添加 insert: " + result);int num = 10 / 0; //算数异常,查看是否会回滚事务return result;}
}

 通过控制台可以看到前面的添加数据的操作执行成功了,后面的代码报异常了

数据库中的数据:

 可以发现数据库中没有新数据的添加,说明事务回滚了

将@Transactional注释掉再次运行程序,数据库就能够在发生异常的时候添加新数据了

 也可以手动进行回滚事务

 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

有一种情况程序发生异常也不会进行回滚,就是程序进行try-catch的时候

上述情况的解决方案:

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager; //事务数据管理器@Autowiredprivate TransactionDefinition transactionDefinition; //设置事务属性的对象@Transactional // 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// todo:非空效验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int result = userService.add(userInfo);System.out.println("添加 insert: " + result);try {int num = 10 / 0;} catch (Exception e) {System.out.println(e.getMessage());// 1.将异常继续抛出throw e;// 2.使用代码手动回滚事务
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
}

 1.将异常抛出

 2.手动回滚事务

两者都不会在数据库中添加新数据

二.事务的4大特性(ACID)

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

三.事务的隔离级别

①Mysql的事务隔离级别:

1. READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回 滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读

2. READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据, 因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间 的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读

3. REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询 的结果⼀致。但也会有新的问题,⽐如此级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败 (因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读 (Phantom Read)。

4. SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决 了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多。

②Spring的事务隔离级别:

1. Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。

2. Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。

3. Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。

4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。

5. Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。

四.事务的传播机制

①事务传播机制的概念

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

事务传播机制时保证一个事务在多个调用方法之间的可控性(稳定性)

②Spring 事务传播机制包含以下 7 种:

1. Propagation.REQUIRED

默认的事务传播机制它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。

示例Ⅰ:方法testA和testB的事务级别为PROPAGATION_REQUIRED,在testA方法里面调用testB方法,testA方法有事务,则在执行testB方法时就加入该事务(当前存在事务,则加入这个事务),此时在执行testB方法的时候如果抛出异常了,由于testA和testB方法是处于同一个事务,所以两者都会发生回滚,数据库里面的数据仍然保持原本状态

示例Ⅱ:方法testA没有声明事务,testB声明了事务且事务级别为Propagation_REQUIRED,在testA方法里面调用testB方法,此时执行testB方法时会自动创建一个事务(如果当前没有事务,则自己创建一个事务),此时在执行testB方法的时候如果抛出异常了,testB方法里面的操作会进行回滚,而testA方法里面的操作不受影响

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager; //事务数据管理器@Autowiredprivate TransactionDefinition transactionDefinition; //设置事务属性的对象@Autowiredprivate LogService logService;@Transactional // 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// todo:非空效验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 添加用户int result = userService.add(userInfo);if(result > 0) {// 添加日志logService.add();}return result;}
}
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer add(UserInfo userInfo) {int result = userMapper.add(userInfo);System.out.println("用户添加: " + result);return result;}
}
@Service
public class LogService {@Transactional(propagation = Propagation.REQUIRED)public int add() {int num = 10 / 0;// 算术异常return 1;}
}

 运行结果:

 

在添加用户完成后进行添加日志,在添加日志里面抛出一个算术异常,令程序回滚,验证添加日志的这个方法已经加入了当前事务中,会整体发生回滚,前面的添加用户操作不生效 

2. Propagation.SUPPORTS

如果当前存在事务,则加⼊该事务(与Propagation_REQUIRED相同);如果当前没有事务,则以⾮事务的⽅式继续运⾏。

示例Ⅰ:方法testA声明了事务,方法testB的事务级别为Propagation_SUPPORTS,在testA方法里面调用testB方法,则在执行testB方法时就加入到testA的事务

示例Ⅱ:方法testA没有声明事务,方法testB的事务级别为Propagation_SUPPORTS,在testA方法里面调用testB方法,则在执行testB方法时就以非事务的方式运行。此时如果testB抛出异常了,也不会发生回滚,所以抛出异常前面的操作能够执行成功

3. Propagation.MANDATORY (mandatory:强制性)

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

示例:方法testA没有声明事务,方法testB的事务级别为Propagation_MANDATORY,testA方法里面调用testB方法,在执行testB方法的时候就会直接抛出事务要求的异常,testB方法里面的操作就没有被执行。

如果testA方法有声明事务且事务级别为Propagation_REQUIRED,则testB方法就会使用testA方法开启的事务,如果在执行testB方法的时候遇见了异常就能够正常的回滚了

4. Propagation.REQUIRES_NEW

表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂 起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开 启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。

示例:方法testA的事务级别为Propagation_REQUIRED,方法testB的事务级别为Propagation_REQUIRES_NEW,testA方法中调用testB方法,在执行testB方法的时候会创建一个新的事务,如果testA方法在调用testB方法之后发生了异常,因为两者不是在同一个事务下,所以不会影响到testB方法中的操作,testB方法中刚刚执行的操作不会进行回滚

5. Propagation.NOT_SUPPORTED

以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。

示例:方法testA的事务级别为Propagation_REQUIRED,方法testB的事务级别为Propagation_NOT_SUPPORTED,testA方法中调用testB方法,因为testB方法是以非事务方式运行的,如果在执行testB方法的时候抛出了异常,testB抛异常前的操作不会回滚,抛异常后的操作不会执行,而在testA方法中执行testB前的操作会由于程序发生了异常而回滚

6. Propagation.NEVER

以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。

示例:方法testA的事务级别为Propagation_REQUIRED,方法testB的事务级别为Propagation_NEVER,testA方法中调用testB方法,因为testB方法的事务级别为Propagation_NEVER,所以已进入testB方法就会抛出事务异常,testA方法检测到了异常就会进行回滚

7. Propagation.NESTED

如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如 果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

示例:方法testA的事务级别为Propagation_REQUIRED,方法testB的事务级别为Propagation_NESTED,testA方法中调用testB方法

Ⅰ.如果执行完testB方法之后,在testA方法中抛出了异常,则testA方法和testB方法中的操作都会进行回滚

Ⅱ.如果异常发生在testB方法里面,因为testA方法里面捕获了testB的异常,所以只有testB方法中的操作会进行回滚,而testA方法中的操作不会受到影响,如果将testB的传播类型改为Propagation_REQUIRED的话,那就testA方法和testB方法都会发生回滚

此处的testA方法就像是老板,testB方法就像是员工,老板公司倒闭了,员工自然就没工作了。而员工犯错了,老板做出应对措施,把员工给开除掉,此级别的事务差不多就是这个意思

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager; //事务数据管理器@Autowiredprivate TransactionDefinition transactionDefinition; //设置事务属性的对象@Autowiredprivate LogService logService;@Transactional // 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// todo:非空效验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 添加用户int result = userService.add(userInfo);if(result > 0) {// 日志try {logService.add();} catch (Exception e) {e.printStackTrace();}}return result;}
}
@Service
public class LogService {@Transactional(propagation = Propagation.NESTED)public int add() {int num = 10 / 0;return 1;}
}
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactional(propagation = Propagation.NESTED)public Integer add(UserInfo userInfo) {int result = userMapper.add(userInfo);System.out.println("用户添加: " + result);return result;}
}

运行结果:用户添加成功

③以情侣关系为例理解这七种传播机制

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

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

相关文章

验证码案例

目录 前言 一、Hutool工具介绍 1.1 Maven 1.2 介绍 1.3 实现类 二、验证码案例 2.1 需求 2.2 约定前后端交互接口 2.2.1 需求分析 2.2.2 接口定义 2.3 后端生成验证码 2.4 前端接收验证码图片 2.5 后端校验验证码 2.6 前端校验验证码 2.7 后端完整代码 前言…

基于可解释性深度学习的马铃薯叶病害检测

数据集来自kaggle文章&#xff0c;代码较为简单。 import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)# Input data files are available in the read-only "../input/" directory # For example, runni…

快团团供货大团长如何查看帮卖团长的订单?

一、功能说明 可以看到团购中每个帮卖团长帮卖产生的订单 二、具体设置方法 1、小程序端如何操作&#xff1f; 在团购页面中&#xff0c;点击订单管理&#xff0c;在这里可以选择全部团长订单&#xff0c;我的团订单&#xff0c;和帮卖团长的帮卖订单。 2、PC端如何操作&am…

ssm616基于vue.js的购物商场的设计与实现+vue【已测试】

前言&#xff1a;&#x1f469;‍&#x1f4bb; 计算机行业的同仁们&#xff0c;大家好&#xff01;作为专注于Java领域多年的开发者&#xff0c;我非常理解实践案例的重要性。以下是一些我认为有助于提升你们技能的资源&#xff1a; &#x1f469;‍&#x1f4bb; SpringBoot…

【基于C++与OpenCV实现魔方图像识别和还原算法】施工总览图

文章目录 主要效果展示思维导图魔方还原算法 本系列博客长期更新&#xff0c;分为两大部分 OpenCV实现魔方六面识别 C编写科先巴二阶段还原算法实现三阶魔方的还原 主要效果展示 摄像头识别六面 3D图像构建&#xff0c;提供还原公式 动画演示还原过程 思维导图 魔方还原算法 参…

达梦8 开启物理逻辑日志对系统的影响

物理逻辑日志&#xff0c;是按照特定的格式存储的服务器的逻辑操作&#xff0c;专门用于 DBMS_LOGMNR 包挖掘获取数据库系统的历史执行语句。当开启记录物理逻辑日志的功能时&#xff0c;这部分日志内 容会被存储在重做日志文件中。 要开启物理逻辑日志的功能&#xff0c;需要…

社区物资交易互助平台的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;基础数据管理&#xff0c;论坛管理&#xff0c;公告信息管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xff0c;求助留言板&#xff0c;公…

SQL Chat:从SQL到SPEAKL的数据库操作新纪元

引言 SQL Chat是一款创新的、对话式的SQL客户端工具。 它采用自然语言处理技术&#xff0c;让你能够像与人交流一样&#xff0c;通过日常对话的形式对数据库执行查询、修改、创建及删除操作 极大地简化了数据库管理流程&#xff0c;提升了数据交互的直观性和效率。 在这个框…

反AI浪潮中的新机遇:Cara艺术社区异军突起

近日,一个名为Cara的艺术社区在网络上迅速走红,其独特的反AI定位吸引了大量创意人士。在AI技术日益普及的今天,Cara社区反其道而行之,致力于打造一个无AI侵害的创作和交流环境。这一创新模式不仅赢得了艺术家的青睐,也为国内创业者提供了新的思考角度。 一、精准定位,守…

C++ list链表的使用和简单模拟实现

目录 前言 1. list的简介 2.list讲解和模拟实现 2.1 默认构造函数和push_back函数 2.2 迭代器实现 2.2.1 非const正向迭代器 2.2.2 const正向迭代器 2.2.3 反向迭代器 2.3 插入删除函数 2.3.1 insert和erase 2.3.2 push_back pop_back push_front pop_front 2.4 构…

[word] word如何清除超链接 #媒体#笔记#知识分享

word如何清除超链接 办公中&#xff0c;少不了使用word&#xff0c;这个是大家必备的软件&#xff0c;今天给大家分享下word如何清除超链接的操作办法&#xff0c;一起来学习下吧&#xff01; 1、清除所有超链接 按下组合键CtrlshiftF9&#xff0c;就可以将网上复制带有超链…

《软件定义安全》之三:用软件定义的理念做安全

第3章 用软件定义的理念做安全 1.不进则退&#xff0c;传统安全回到“石器时代” 1.1 企业业务和IT基础设施的变化 随着企业办公环境变得便利&#xff0c;以及对降低成本的天然需求&#xff0c;企业始终追求IT集成设施的性价比、灵活性、稳定性和开放性。而云计算、移动办公…

pytorch 加权CE_loss实现(语义分割中的类不平衡使用)

加权CE_loss和BCE_loss稍有不同 1.标签为long类型&#xff0c;BCE标签为float类型 2.当reduction为mean时计算每个像素点的损失的平均&#xff0c;BCE除以像素数得到平均值&#xff0c;CE除以像素对应的权重之和得到平均值。 参数配置torch.nn.CrossEntropyLoss(weightNone,…

解决Windows窗口聚焦问题

情景引入&#xff1a; 在使用副屏显示器写代码&#xff0c;主屏显示器看教程的时候&#xff0c;突然有个知识点卡住了&#xff0c;这个时候你想要按下空格让视频暂停&#xff0c;但是按下后你会发现&#xff1a;视频没有暂停&#xff0c;倒是代码界面多了个空格。。。这就不好玩…

用HTML实现拓扑面,动态4D圆环面,可手动调节,富有创新性的案例。(有源代码)

文章目录 前言一、示例二、目录结构三、index.html&#xff08;主页面&#xff09;四、main.js五、Tour4D.js六、swissgl.js七、dat.gui.min.js八、style.css 前言 如果你觉得对代码进行复制粘贴很麻烦的话&#xff0c;你可以直接将资源下载到本地。无需部署&#xff0c;直接可…

如何对stm32查看IO功能。

有些同学对于别人的开发板的资源&#xff0c;或者IO口&#xff0c;或者串口等资源不知道怎么分配。 方法1、看硬石、野火、正点原子的开发板&#xff0c;看下他们的例子&#xff0c;那个资源用什么。自己多看几个原理图&#xff0c;多看几个视频&#xff0c;做一下笔记。以后依…

【面试干货】MySQL 三种锁的级别(表级锁、行级锁和页面锁)

【面试干货】MySQL 三种锁的级别&#xff08;表级锁、行级锁和页面锁&#xff09; 1、表级锁2、行级锁3、页面锁4、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在 MySQL 数据库中&#xff0c;锁是控制并发访问的重要机制&#xff0…

GQA,MLA之外的另一种KV Cache压缩方式:动态内存压缩(DMC)

0x0. 前言 在openreview上看到最近NV的一个KV Cache压缩工作&#xff1a;https://openreview.net/pdf?idtDRYrAkOB7 &#xff0c;感觉思路还是有一些意思的&#xff0c;所以这里就分享一下。 简单来说就是paper提出通过一种特殊的方式continue train一下原始的大模型&#x…

DS:树与二叉树的相关概念

欢迎来到Harper.Lee的学习世界&#xff01;博主主页传送门&#xff1a;Harper.Lee的博客主页想要一起进步的uu可以来后台找我哦&#xff01; 一、树的概念及其结构 1.1 树的概念亲缘关系 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限节点…

汇编:数组-寻址取数据

比例因子寻址&#xff1a; 比例因子寻址&#xff08;也称为比例缩放索引寻址或基址加变址加比例因子寻址&#xff09;是一种复杂的内存寻址方式&#xff0c;常用于数组和指针操作。它允许通过一个基址寄存器、一个变址寄存器和一个比例因子来计算内存地址。 语法 比例因子寻…