Spring中事务的隔离级别和传播机制

上一篇博客中讲解了关于事务的两种使用方式包括@Transactional的详解。

@Transactional 注解当中的三个常⻅属性:
1. rollbackFor: 异常回滚属性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型
2. Isolation: 事务的隔离级别. 默认值为 Isolation.DEFAULT
3. propagation: 事务的传播机制. 默认值为 Propagation.REQUIRED

关于第二点和第三点还没有讲解完,这一篇博客来讲解关于事务的隔离级别和传播机制。


1. 事务的隔离级别

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

原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

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

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

隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatableread)和串行化(Serializable)。

其中隔离性在我之前的博客中也有讲解,建议去看看:
MySQL事务的四种隔离级别详解-CSDN博客文章浏览阅读1k次,点赞33次,收藏20次。MySQL为我们提供了不同的“隔离级别”,这样就方便使用者控制隔离级别的高低/并发程度的高低/执行效率的高低/准确性的高低。因为MySQL服务器是要给多个客户端来使用的,那么此时多个客户端之间就会同时发起事务,尤其是发起的多个事务在操作同一个数据库的同一个表的时候,这样由于“多线程”的操作就很容易引发一些“意料之外的”麻烦。如果隔离性越高,就意味着事务之间的并发程度越低,执行效率越慢,但是数据的准确性是越高的。如果隔离性越低,就意味着事务时间的并发程度越高,执行效率越快,但是数据的准确性是越低的。https://blog.csdn.net/qq_45875349/article/details/136722597我们为什么要设置隔离级别?

设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操作者预期的。

2. Spring中设置事务的隔离级别

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置,具体操作如下图所示:
在学习Spring中的事务隔离级别前,先回顾一下刚才我的博客中提到的MySQL中的隔离级别:
MySQL 事务隔离级别有 4 种
  1. READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
  2. READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据。因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
  3. REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同一事务多次查询只的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读(Phantom Read)。
  4. SERIALIZABLE:序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突、从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。

 在数据库中通过以下 SQL 查询全局事务隔离级别和当前连接的事务隔离级别:

select @@global.tx_isolation,@@tx_isolation;
Spring 事务隔离级别有 5 种
⽽ Spring 中事务隔离级别包含以下 5 种:
  • Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
  • Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
  • Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
  • Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。
  • Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。
从上述介绍可以看出,相⽐于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个。
Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置:

3. Spring事务传播机制

3.1 什么是事务的传播机制

事务传播机制就是: 多个事务方法存在调用关系时, 事务是如何在这些方法间进⾏传播的。
比如有两个⽅法A, B都被 @Transactional 修饰, A方法调用B⽅法, A方法运行时, 会开启⼀个事务. 当A调用B时, B方法本⾝也有事务, 此时B方法运行时, 是加入A的事务, 还是创建⼀个新的事务呢?
这个就涉及到了事务的传播机制!
⽐如公司流程管理
执⾏任务之前, 需要先写执⾏⽂档, 任务执⾏结束, 再写总结汇报
此时A部⻔有⼀项⼯作, 需要B部⻔的⽀援, 此时B部⻔是直接使⽤A部⻔的⽂档, 还是新建⼀个⽂档呢?

事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题  

⽽事务传播机制解决的是⼀个事务在多个节点(方法)中传递的问题:

3.2 事务的传播机制有哪些

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

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

3.3 Spring事务传播机制演示

3.3.1 REQUIRED(默认值)

如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

比如现在我们有两个银行账户A001和A002,进行转账交易。一个转账的业务可分为两个步骤:

1.A001从自己的账户扣款;

2.A002的账户加上A001扣的款。

如果有任何一步骤失败了,都应该全部回滚。因为如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。两步操作肯定是在同一事务中。

controller代码:

package com.example.transactiondemo.controller;import com.example.transactiondemo.service.BankService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/bank")
public class BankController {@Autowiredprivate BankService bankService;@PostMapping("/transfer")@Transactional(propagation = Propagation.REQUIRED)public String transfer(@RequestParam String fromAccount, @RequestParam String toAccount, @RequestParam double amount) {//从账户A001扣钱bankService.transfer1(fromAccount, amount);//给账户A002加钱bankService.transfer2(toAccount, amount);return "Transfer successful";}
}

service代码:

package com.example.transactiondemo.service;import com.example.transactiondemo.entity.Account;
import com.example.transactiondemo.mapper.AccountMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service
public class BankService {@Resourceprivate AccountMapper accountMapper;@Transactional(propagation = Propagation.REQUIRED)public void transfer1(String fromAccountNumber, double amount) {Account fromAccount = accountMapper.findByAccountNumber(fromAccountNumber);if (fromAccount == null ) {throw new IllegalArgumentException("Invalid account number");}if (fromAccount.getBalance() < amount) {throw new IllegalArgumentException("Insufficient balance in account: " + fromAccountNumber);}fromAccount.setBalance(fromAccount.getBalance() - amount);accountMapper.updateAccount(fromAccount);}@Transactional(propagation = Propagation.REQUIRED)public void transfer2(String toAccountNumber, double amount) {Account toAccount = accountMapper.findByAccountNumber(toAccountNumber);if (toAccount == null) {throw new IllegalArgumentException("Invalid account number");}toAccount.setBalance(toAccount.getBalance() + amount);accountMapper.updateAccount(toAccount);// 手动引入异常来测试事务回滚if (amount > 500) {throw new RuntimeException("Transfer amount exceeds limit, transaction will be rolled back");}}
}

注意这个手动加入的异常是伪代码,没有实际意义,为了演示效果。

Mapper代码:

package com.example.transactiondemo.mapper;import com.example.transactiondemo.entity.Account;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;@Mapper
public interface AccountMapper {Account findByAccountNumber(@Param("accountNumber") String accountNumber);void updateAccount(Account account);
}

xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.transactiondemo.mapper.AccountMapper"><select id="findByAccountNumber" resultType="com.example.transactiondemo.entity.Account">SELECT id, account_number AS accountNumber, balanceFROM accountWHERE account_number = #{accountNumber}</select><update id="updateAccount">UPDATE accountSET balance = #{balance}WHERE id = #{id}</update>
</mapper>

使用POSTMAN测试:

第一次转账100应该成功,没有触发异常都正常提交:

第二次转账600应该失败,触发了异常(手动),数据库中没有任何变化:

因为,上述操作的执行流程大概是:

其他事务传播机制的使用都大同小异,主要还是根据不同的场景来觉得使用什么类型的传播机制。


总 结

1. 通过 @Transactional(isolation = Isolation.SERIALIZABLE) 设置事务的隔离级 别. Spring 中的事务隔离级别有 5 种
2. 通过 @Transactional(propagation = Propagation.REQUIRED) 设置事务的传播机制, Spring 中的 事务传播级别有 7 种, 重点关注 REQUIRED (默认值) 和 REQUIRES_NEW

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

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

相关文章

[whl]树莓派armv7l文件onnx的whl所有下载地址汇总

下面onnx所有版本都是树莓派armv7l平台的whl文件&#xff0c;不能用于windows,macOS或者ubuntu x86_64的系统&#xff0c;请认准文件名及其对应python版本下载。 序号版本名称下载地址1onnx-1.16.1-cp311-cp311-linux_armv7l.whl点我下载2onnx-1.16.0-cp311-cp311-linux_armv7…

短视频矩阵系统:高效运营,解决多账号管理难题

前言 在当下短视频风靡的时代&#xff0c;如何高效管理和运营多个短视频账号&#xff0c;成为了众多运营者面临的挑战。而今&#xff0c;一款全新的短视频矩阵系统应运而生&#xff0c;它不仅融合了AI文案生成与剪辑模式等先进功能&#xff0c;更支持多平台授权&#xff0c;助…

超分辨率重建——2022冠军RLFN网络推理测试(详细图文教程)

&#x1f4aa; 专业从事且热爱图像处理&#xff0c;图像处理专栏更新如下&#x1f447;&#xff1a; &#x1f4dd;《图像去噪》 &#x1f4dd;《超分辨率重建》 &#x1f4dd;《语义分割》 &#x1f4dd;《风格迁移》 &#x1f4dd;《目标检测》 &#x1f4dd;《暗光增强》 &a…

如何用Vue3打造一个炫酷的树状图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于 Vue.js 的 Treemap 可视化组件 应用场景介绍 Treemap 可视化组件是一种强大的工具&#xff0c;用于以直观的方式展示分层数据。它将数据点绘制为矩形&#xff0c;其中每个矩形的大小与数据点的大小成正比…

阿里云平台创建设备及连接

使用阿里云平台创建项目&#xff0c;利用MQTT.fx软件配置相关的连接&#xff0c;在软件上完成消息的订阅与推送&#xff0c;与手机APP进行同步数据。了解MQTT相关的协议。 1.注册阿里云平台账号&#xff0c;完成实名注册&#xff01; 618创新加速季_新迁入云享5亿算力补贴-阿里…

如何写出一份出色的论文开题报告

论文开题报告是研究生阶段一个关键的环节,它直接影响着后续论文的质量。一份好的开题报告不仅能让导师及评审专家对你的研究方向和内容有一个全面的了解,还能帮助你梳理思路,为顺利开展研究奠定基础。下面就让我们一起来看看,要如何写好一份开题报告。 开题报告的主要内容 一…

JCR一区 | Matlab实现GAF-PCNN、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断

JJCR一区 | Matlab实现GAF-PCNN、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断 目录 JJCR一区 | Matlab实现GAF-PCNN、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断分类效果格拉姆矩阵图GAF-PCNNGASF-CNNGADF-CNN 基本介绍程序设计参考资料 分类效果 格拉姆…

【网络编程开发】16.域名解析与http服务器实现原理

16.域名解析与http服务器实现原理 文章目录 16.域名解析与http服务器实现原理gethostbyname 函数HTTP的操作过程实现httphome.htmlhttp-head.txtserver.c gethostbyname 函数 原型&#xff1a; #include <netdb.h> struct hostent *gethostbyname(const char *hostname);…

技巧|手机上看SwanLab实验的两种方法

什么是SwanLab? SwanLab是一个深度学习实验管理与训练可视化工具&#xff0c;由西安电子科技大学创业团队打造&#xff0c;融合了Weights & Biases与Tensorboard的特点&#xff0c;可以记录整个实验的超参数、指标、训练环境、Python版本等&#xff0c;并可视化图表&…

Java代码运行的原理

Java的运行原理主要涉及编译、类加载、字节码执行和垃圾回收。以下是Java代码的运行流程和关键组件的详细解释&#xff1a; 1. 编写代码 开发者使用文本编辑器或IDE编写Java源代码&#xff0c;文件扩展名为.java。例如&#xff1a; public class HelloWorld {public static …

google chrome浏览器安装crx插件Jam

先上一张图&#xff1a; Jam是bug报告生成插件 1、在地址栏中输入chrome://extensions/&#xff0c;然后回车。 2、将下载好的crx插件&#xff0c;直接拖到里面就可以完成安装工作了。 3、测试了一下jam插件&#xff0c;发现直接没有响应。 4、点击【移除】直接可以删除插件…

C++ 子集合枚举

给定一个正整数数组 nums[], 求所有可能的组合&#xff0c;使得组合中的元素和等于target, 例如: nums 为 {3, 4, 5}, target 为 9, 解为 {3, 3, 3}, {4, 5} #include <algorithm> #include <memory> #include <string> #include <vector> #include…

这四个有意思的工具,很香

提醒英雄 提醒英雄应用是一款能够帮助用户彻底解决健忘症的应用程序。该应用创建的事项会完全同步到通知中心&#xff0c;并且持续保持在锁屏界面上&#xff0c;只要打开手机&#xff0c;用户就会看到之前设置的提醒事项。这种设计确保了用户在任何时候都能及时收到提醒&#…

YOLOv10改进 | 注意力篇 | YOLOv10引入Polarized Self-Attention注意力机制

1. Polarized Self-Attention介绍 1.1 摘要:像素级回归可能是细粒度计算机视觉任务中最常见的问题,例如估计关键点热图和分割掩模。 这些回归问题非常具有挑战性,特别是因为它们需要在低计算开销的情况下对高分辨率输入/输出的长期依赖性进行建模,以估计高度非线性的像素语…

uniapp——微信扫码进入小程序、H5页面,获取数据

进入小程序 微信调起扫一扫&#xff0c;扫描一个二维码码进入小程序 【http://www.love.com/?id20】 onLoad((options) > {if (options.q) {let url decodeURIComponent(options.q)id.value i.getUrlKey(id, url)getDetails()return} })进入H5 微信调起扫一扫&#xf…

【ARMv8/ARMv9 硬件加速系列 2.3 -- ARM NEON 的四舍五入指令】

文章目录 NEON 的四舍五入SRSHLR 指令格式SRSHLR 操作说明SRSHLR 示例解释NEON 的四舍五入 SRSHR指令是ARMv8 NEON SIMD指令集中的一部分,用于对向量中的每个元素进行向右的算术位移操作,并将结果四舍五入。SRSHR指令的全称是Signed Rounding Shift Right,适用于带符号的整…

JavaFX DatePicker

JavaFX DatePicker允许从给定日历中选择一天。DatePicker控件包含一个带有日期字段和日期选择器的组合框。JavaFX DatePicker控件使用JDK8日期时间API。 import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.DatePicker; import j…

基础语法总结 —— Python篇

1、环境搭建 建议直接安装 PyCharm &#xff08;Community Edition&#xff09; Python3.x版本&#xff0c;前者是一个很好用的编译器&#xff0c;后者是Python的运行环境之类的&#xff0c;安装参考https://mp.csdn.net/mp_blog/creation/editor/139511640 2、标识符 第一个…

AWS无服务器 应用程序开发—第十二章 AWS Step Functions

AWS Step Functions 是一种服务,用于协调和管理分布式应用程序中的多个 AWS 服务和 Lambda 函数。它通过创建有状态的工作流来简化和自动化应用程序的各种工作流程,使得复杂的业务逻辑可以以可管理和可调试的方式实现。 主要功能和特点: 状态机定义: 使用 JSON 或 Amazo…

Django与Flask的区别:从开发者视角的深度探讨

Django与Flask的区别&#xff1a;从开发者视角的深度探讨 在现代Web开发中&#xff0c;Python的两大热门框架Django和Flask&#xff0c;常常引起开发者的热烈讨论。作为一个在Python生态系统中进行Web开发的技术员&#xff0c;选择适合的框架至关重要。今天&#xff0c;我将从…