Spring—事务及事务的传播机制

Spring—事务及事务的传播机制

  • 🔎事务的定义
  • 🔎Spring—事务的实现
    • 铺垫
    • Spring 编程式事务
    • Spring 声明式事务
      • @Transactional 的参数
      • 注意事项
      • @Transactional 的工作原理
  • 🔎Spring—事务的隔离级别
    • MySQL—事务的隔离级别
    • Spring—事务的隔离级别
    • Spring—设置事务的隔离级别
  • 🔎Spring—事务的传播机制
    • Spring—事务传播机制的分类
    • 对比加入事务与嵌套事务
      • 总结加入事务与嵌套事务之间的区别

🔎事务的定义


将一组操作封装成一个执行单元, 即这一组操作一同成功 / 一同失败

举个栗子🌰

未使用事务

滑稽老哥给女神转账 520
由于某种原因, 女神并未收到转账的 520, 而滑稽老哥却被扣款 520

使用事务

滑稽老哥给女神转账 520
由于某种原因, 女神并未收到转账的 520
因为使用事务, 所以滑稽老哥的钱也被重新打回账户上
(一同成功 / 一同失败)

🔎Spring—事务的实现


Spring—事务的实现有 2 种方式

  1. 通过代码的方式手动实现事务(即 Spring 编程式事务)
  2. 通过注解的方式实现事务(即 Spring 声明式事务)

铺垫


后续内容针对数据表 userinfo 进行演示🍂

-- 创建用户表
drop table if exists userinfo;
create table userinfo(id int primary key auto_increment,username varchar(100) not null,password varchar(32) not null,photo varchar(500) default '',createtime timestamp default current_timestamp,updatetime timestamp default current_timestamp,`state` int default 1
) default charset 'utf8mb4';-- 添加一个用户信息
INSERT INTO `excnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) 
VALUES (1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);

在这里插入图片描述


Controller 层(UserController) → Service 层(UserService) → Mapper 层(UserMapper)🍂

在这里插入图片描述

UserController

代码在 Spring 编程式事务

UserService

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public Integer add(UserInfo userInfo) {return userMapper.add(userInfo);}}

UserMapper

@Mapper
public interface UserMapper {int add(UserInfo userInfo);}

UserMapper 对应的 SQL 语句

<insert id="add">insert into userinfo(username, password) values(#{username}, #{password})
</insert>

UserInfo

@Data
public class UserInfo {private Integer id;private String username;private String password;private String photo;private String createtime;private String updatetime;private Integer state;}

Spring 编程式事务


Spring 编程式事务与 MySQL 操作事务类似, 分为 3 个部分

  1. 开启事务
  2. 提交事务
  3. 回滚事务
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/add")public Integer add(UserInfo userInfo) {// 非空校验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 1. 开启事务TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);// 执行相关业务代码int ret = userService.add(userInfo);System.out.println("add : " + ret);// 2. 提交事务transactionManager.commit(transactionStatus);// 3. 回滚事务transactionManager.rollback(transactionStatus);return ret;}}

验证效果(开启事务 + 提交事务)🍂

在这里插入图片描述

验证效果(开启事务 + 回滚事务)🍂

在这里插入图片描述

Spring 声明式事务


利用注解 @Transactional 实现 Spring 声明式事务

@Transactional 的特点

  • 可以添加在类 / 方法上(默认情况下 @Transactional 针对的是 public 修饰的方法)
  • 方法执行前自动开启事务, 方法执行期间出现异常 → 自动回滚事务, 方法执行完(无异常) → 自动提交事务

举个栗子🌰

方法执行期间出现异常 → 自动回滚事务

在这里插入图片描述

在这里插入图片描述

方法执行完(无异常) → 自动提交事务

在这里插入图片描述

在这里插入图片描述

@Transactional 的参数


在这里插入图片描述

参数作用
value配置多个事务管理器时, 可指定需要的事务管理器
transactionManager配置多个事务管理器时, 可指定需要的事务管理器
propagation事务的传播行为, 默认值为 Propagation.REQUIRED
isolation事务的隔离级别, 默认值为 Isolation.DEFAULT
timeout事务的超时时间, 默认值为 -1, 表示超时时间为无限大, 即事务将一直持续直到完成或手动回滚(如果超出设置的时间限制事务仍未完成, 则自动回滚事务)
readOnly指定事务是否为只读, 默认值为 false(为了忽略不需要事务的方法, 例如读取数据, 设置 read-only 为 true)
rollbackFor用于指定能够触发事务回滚的异常类型(可指定多个异常类型)
rollbackForClassName用于指定能够触发事务回滚的异常类型(可指定多个异常类型)
noRollbackFor抛出指定的异常类型, 不回滚事务(可指定多个异常类型)
noRollbackForClassName抛出指定的异常类型, 不回滚事务(可指定多个异常类型)

注意事项


方法内部存在异常, 但被捕获(try-catch)时, 仍会提交事务

即方法执行完(无异常) → 自动提交事务

代码示例如下

@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/insert")@Transactionalpublic Integer insert(UserInfo userInfo) {// 非空校验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int ret = userService.add(userInfo);System.out.println("insert : " + ret);try {int num = 2 / 0;} catch (Exception e) {System.out.println(e.getMessage());}return ret;}}

针对上述情况, 想要回滚事务, 有 2 种解决方式

  1. 继续抛出异常
  2. 手动回滚事务(推荐)

继续抛出异常🍂

@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/insert")@Transactionalpublic Integer insert(UserInfo userInfo) {// 非空校验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int ret = userService.add(userInfo);System.out.println("insert : " + ret);try {int num = 2 / 0;} catch (Exception e) {System.out.println(e.getMessage());// 抛出异常throw e;}return ret;}}

验证效果(抛出异常)

在这里插入图片描述

手动回滚事务🍂

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/insert")@Transactionalpublic Integer insert(UserInfo userInfo) {// 非空校验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int ret = userService.add(userInfo);System.out.println("insert : " + ret);try {int num = 2 / 0;} catch (Exception e) {System.out.println(e.getMessage());// 手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return ret;}}

验证效果(手动回滚事务)

在这里插入图片描述

@Transactional 的工作原理


@Transactional 是基于 AOP 实现, AOP 使用动态代理实现

目标对象实现了接口, 默认采用 JDK 的动态代理 / 目标对象未实现接口, 默认采用 CGLIB 的动态代理

@Transactional 在开始执行业务之前, 通过代理开启事务, 执行成功之后提交事务(执行期间遇到异常回滚事务)


在这里插入图片描述

🔎Spring—事务的隔离级别


事务有 4 大特性(ACID)

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

原子性🍂

定义

⼀个事务(Transaction)中的所有操作, 要么全部完成, 要么全部不完成, 不会结束在中间某个环节. 事务在执⾏过程中发⽣错误, 会被回滚(Rollback)到事务开始前的状态, 就像这个事务从来没有执⾏过⼀样

翻译

用户 A 给用户 B 转账
要么 A 成功转账给 B, B 收到转账的钱
要么 A 没能转账给 B, B 未收到转账的钱
不能出现 A 成功转账给 B, B 未收到钱等类似的情况

一致性🍂

定义

在事务开始之前和事务结束以后, 数据库的完整性没有被破坏. 这表示写⼊的资料必须完全符合所有的预设规则, 这包含资料的精确度, 串联性以及后续数据库可以⾃发性地完成预定的工作

翻译

用户 A 给用户 B 转账 520
A 账户被扣款 520, B 账户增加 520
不能出现 A 账户扣款 1000, B 账户增加 520 等类似的情况

隔离性🍂

定义

数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒, 隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不⼀致. 事务隔离分为不同级别, 包括读未提交(Read Uncommitted), 读已提交(Read Committed), 可重复读(Repeatable Read)和串行化(Serializable)

翻译(摘抄自网络)

将点餐过程理解为一个事务
制作 → 出餐 → 送餐 → 结算作为一个事务

不同的客户进行点餐是相互独立的, 并不会彼此影响 → 事物的隔离性

当一个事务影响其他事务时, 其他事务将会回滚

今日份的猪脚饭已经全部卖完, 再次点餐将会影响后续事务(制作 → 出餐 → 送餐 → 结算), 此时后续事务将会回滚

持久性🍂

定义

事务处理结束后, 对数据的修改就是永久的, 即便系统故障也不会丢失

翻译

用户 A 执行转账操作, 银行系统会开启事务, 将转账金额从 A 账户扣除, 将对应金额添加至 B 账户
当事务提交成功后, 系统会将更改持久化到数据库
即使系统发生故障, 数据库仍然能够恢复并保持转账操作的结果

MySQL—事务的隔离级别


MySQL—事务的隔离级别🍂

  1. READ UNCOMMITTED → 读未提交
  2. READ COMMITTED → 读已提交
  3. REPEATABLE READ → 可重复读(MySQL 默认的事务隔离级别)
  4. SERIALIZABLE → 串行化
事务隔离级别脏读不可重复读幻读
读未提交(READ UNCOMMITTED)
读已提交(READ COMMITTED)
可重复读(REPEATABLE READ)
串行化(SERIALIZABLE)

Spring—事务的隔离级别


在这里插入图片描述


Spring—事务的隔离级别🍂

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

Spring—设置事务的隔离级别


@Transactional(isolation = Isolation.DEFAULT)
public void setIsolationLevel() {}

🔎Spring—事务的传播机制


事务的传播机制 → 事务的隔离级别 Plus 版

事务的隔离级别🍂

解决多个事务同时调用数据库的问题

在这里插入图片描述

事务的传播机制

解决一个事务在多个方法中传递的问题

在这里插入图片描述

Spring—事务传播机制的分类


Spring—事务传播机制的分类🍂

  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

在这里插入图片描述


举个栗子🌰

在这里插入图片描述


对比加入事务与嵌套事务


在这里插入图片描述


UserController

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate LoginService loginService;@RequestMapping("/insert")@Transactionalpublic Integer insert(UserInfo userInfo) {// 非空校验if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}int ret = userService.add(userInfo);if(ret > 0) {loginService.add();}return ret;}}

加入事务🍂

@Transactional(propagation = Propagation.REQUIRED)为例

UserService

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactional(propagation = Propagation.REQUIRED)public Integer add(UserInfo userInfo) {int ret = userMapper.add(userInfo);System.out.println("添加 : " + ret);return ret;}}

LoginService

@Service
public class LoginService {@Transactional(propagation = Propagation.REQUIRED)public Integer add() {try {int num = 1 / 0;} catch (Exception e) {System.out.println(e.getMessage());TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return -1;}}

验证效果(执行期间出现异常 → 回滚全部事务)

在这里插入图片描述

嵌套事务🍂

@Transactional(propagation = Propagation.为例

UserService

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactional(propagation = Propagation.NESTED)public Integer add(UserInfo userInfo) {int ret = userMapper.add(userInfo);System.out.println("添加 : " + ret);return ret;}}

LoginService

@Service
public class LoginService {@Transactional(propagation = Propagation.NESTED)public Integer add() {try {int num = 1 / 0;} catch (Exception e) {System.out.println(e.getMessage());TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return -1;}}

验证效果(执行期间出现异常 → 回滚部分事务)

在这里插入图片描述

总结加入事务与嵌套事务之间的区别


加⼊事务(REQUIRED) 和 嵌套事务(NESTED) 的区别

  • 整个事务如果全部执行成功,结果均相同
  • 如果事务执行期间失败了, 那么加入事务会将整个事务全部回滚;嵌套事务则会局部回滚,不会影响上⼀个方法中执行的结果

🌸🌸🌸完结撒花🌸🌸🌸

在这里插入图片描述

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

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

相关文章

Prometheus学习

Prometheus学习 promethueus exporter就是以 后台进程的方式运行在系统当中&#xff0c;不断去采集数据 1、红框框中是 prometheus基于数学算法输入的一个查询输入框 2.监控项的分类 3.数据采集的形式分类&#xff08;promethueus exporter就是以 后台进程的方式运行在系统当…

前端vue入门(纯代码)27_路由的query参数

安静地努力&#xff01;&#xff01;&#xff01; 【25.Vue Router--路由的query参数】 多级路由在src/router/index.js中【三级路由】的配置如下&#xff1a; // 该文件专门用于创建整个应用的路由器 import VueRouter from "vue-router"; //引入组件 import Abo…

springboot家政服务管理平台

本系统为了数据库结构的灵活性所以打算采用MySQL来设计数据库&#xff0c;而java技术&#xff0c;B/S架构则保证了较高的平台适应性。本文主要介绍了本系统的开发背景&#xff0c;所要完成的功能和开发的过程&#xff0c;主要说明了系统设计的重点、设计思想。 本系统主要是设…

Docker学习笔记23

Docker Swarm架构&#xff1a; Swarm中以集群&#xff08;Cluster&#xff09;为单位进行管理&#xff0c;支持服务层面的操作。 集群是Swarm所管理的对象。 基本概念&#xff1a; 节点&#xff08;Node&#xff09;为Swarm集群中的一个Docker Engine实例。其中管理节点&#…

Elasticsearch索引库、文档操作

一、索引库操作 1、创建索引库&#xff1a; #创建索引库 PUT /索引库名 {"mappings": {"properties": {"info":{"type": "text","analyzer": "ik_smart"},"email":{"type": "…

前端工程打包部署

打包 直接执行第二个脚本build即可 打包后的文件将会放在dist目录下 部署 NGINX&#xff1a;是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。其特点是占用内存少&#xff0c;并发能力强&#xff0c;在各大型互联网公司都有非…

JDBC技术【JDBC概述、获取数据库连接、 下载数据库驱动】(一)-全面详解(学习总结---从入门到深化)

目录 JDBC概述 JDBC编写步骤 获取连接 下载数据库驱动 获取数据库连接 Properties文件的使用 JDBC概述 数据的持久化 持久化(persistence)&#xff1a;将内存中的数据保存到可永久保存的存储 设备中&#xff08;如磁盘&#xff09;。 持久化的主要应用是将内存中的数据存储…

微信小程序canvas生成图片并保存

需求&#xff1a; 做一个类似下图的功能。图片内容是动态的&#xff0c;用canvas画出来&#xff0c;生成临时图片&#xff0c;再保存。 实现&#xff1a; <view class"canvasBox"><canvas canvas-id"myCanvas" class"myCanvas">&l…

B. Building Company

Problem - B - Codeforces 思路&#xff1a;我们能够发现这其实类似于操作系统的问题&#xff0c;其思想就是我们先把能完成的工程完成&#xff0c;然后加入完成工程后得到的奖励&#xff0c;然后再看是否会有新的工程能够完成&#xff0c;然后一直重复知道不会再出现新的工程能…

Unity/Shader 零碎知识点

来源&#xff1a;shader入门精要-冯乐乐 坐标系 Unity使用的是左手坐标系&#xff1b;观察空间&#xff0c;通俗来讲就是以摄像机为原点的坐标系&#xff0c;摄像机的前向是z轴的负方向&#xff0c;与模型和世界空间中的定义相反&#xff0c;z轴的坐标减少意味着场景深度的增加…

【动手学习深度学习--逐行代码解析合集】08模型选择、欠拟合和过拟合

【动手学习深度学习】逐行代码解析合集 08模型选择、欠拟合和过拟合 视频链接&#xff1a;动手学习深度学习–模型选择、欠拟合和过拟合 课程主页&#xff1a;https://courses.d2l.ai/zh-v2/ 教材&#xff1a;https://zh-v2.d2l.ai/ 1、生成数据集 import math import numpy a…

PROFINET转ETHERNET/IP网关西门子通讯协议profinet

大家好&#xff0c;今天我们来聊一款令人兴奋的产品——远创智控YC-PN-EIP&#xff01;它是一款自主研发的 PROFINET 从站功能的通讯网关&#xff0c;可以将 PROFINET网络和ETHERNET/IP 网络连接起来&#xff0c;实现数据传输和交换。但这只是它的基础功能&#xff0c;它还有哪…