深入理解Spring事务机制

Spring事务机制

  • 一:故事背景
  • 二:核心知识
    • 2.1 Spring事务种类
      • 2.2.1 编程式事务
      • 2.2.2 声明式事务
    • 2.2 Spring事务隔离级别
    • 2.3 Spring事务传播机制
      • 2.3.1 概念
      • 2.3.2 七种事务传播机制
    • 2.4 Spring声明式事务实现原理
      • 2.4.1 Bean初始化创建代理对象
      • 2.4.2 执行目标方法时进行事务增强
    • 2.5 Spring声明式事务失效场景
      • 2.5.1 应用在非Public的方法上
      • 2.5.2 属性propagation设置错误
      • 2.5.3 rollbackFor设置错误
      • 2.5.4 同一个类中方法调用
  • 三:总结提升

一:故事背景

本文将重点分享Spring事务相关知识,通过这篇文章,了解Spring事务的实现原理,让你以后在开发中,使用的有底气,有依据。

二:核心知识

2.1 Spring事务种类

2.2.1 编程式事务

  1. 编程式事务是一种在代码中显式地编写事务管理逻辑的方法,相对于声明式事务来说,更加灵活但也更加繁琐。在编程式事务中,开发者需要通过编写代码来控制事务的开始、提交和回滚。
  2. Spring框架同样提供了编程式事务管理的支持,通常通过编程式事务管理接口来实现,例如PlatformTransactionManager。

例子:

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;@Component
public class BankService {private PlatformTransactionManager transactionManager;public void setTransactionManager(PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}public void transferFunds(String fromAccount, String toAccount, double amount) {TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());try {// 执行转账操作,更新账户余额// 省略具体的转账业务逻辑,假设 updateAccountBalance 方法用于更新账户余额updateAccountBalance(fromAccount, -amount);updateAccountBalance(toAccount, amount);transactionManager.commit(txStatus);} catch (Exception e) {transactionManager.rollback(txStatus);throw e;}}private void updateAccountBalance(String account, double amount) {// 更新账户余额的具体实现}
}

2.2.2 声明式事务

我们最常用的就是声明式事务,这里列出3点使用的注意事项:

  1. 声明式事务使用@Transactional 注解的方式
  2. 声明式事务的管理是建立在AOP上的,本质是通过AOP功能,对方法的前后进行拦截,将事务的处理编织到拦截的方法中去。
  3. 通过@Transaction最细的粒度只能做到方法的级别,在目标方法执行前开启事务,执行完成后提交事务,出现错误回滚事务。

例子:

import org.springframework.transaction.annotation.Transactional;@Component
public class BankService {@Transactionalpublic void transferFunds(String fromAccount, String toAccount, double amount) {// 执行转账操作,更新账户余额// 省略具体的转账业务逻辑,假设 updateAccountBalance 方法用于更新账户余额updateAccountBalance(fromAccount, -amount);updateAccountBalance(toAccount, amount);}private void updateAccountBalance(String account, double amount) {// 更新账户余额的具体实现}
}

注意如果@Transactional加到方法上的话,会对指定方法进行事务管理、如果加到类上的话,会对这个类所有的public都进行事务管理。

2.2 Spring事务隔离级别

TransactionDefinition 接口定义了事务的各种属性,如传播行为、隔离级别、超时时间等,其实主要还是对应数据库的事务隔离级别。
我们来看一下TransactionDefinition 对应源码

/*** 定义了事务的属性,包括传播行为、隔离级别、超时时间等。*/
public interface TransactionDefinition {int PROPAGATION_REQUIRED = 0;            // 当前方法必须在一个事务中执行,如果没有事务则创建一个int PROPAGATION_SUPPORTS = 1;            // 当前方法支持在一个事务中执行,如果没有事务也可以int PROPAGATION_MANDATORY = 2;           // 当前方法必须在一个事务中执行,如果没有事务则抛出异常int PROPAGATION_REQUIRES_NEW = 3;        // 当前方法必须在一个新的事务中执行,如果已存在事务则挂起它int PROPAGATION_NOT_SUPPORTED = 4;       // 当前方法不应该在事务中执行,如果存在事务则挂起它int PROPAGATION_NEVER = 5;               // 当前方法不应该在事务中执行,如果存在事务则抛出异常int PROPAGATION_NESTED = 6;              // 当前方法必须在一个嵌套事务中执行int ISOLATION_DEFAULT = -1;              // 使用默认的隔离级别int ISOLATION_READ_UNCOMMITTED = 1;      // 读未提交的数据,可能导致脏读、不可重复读、幻读int ISOLATION_READ_COMMITTED = 2;        // 读已提交的数据,可以避免脏读,但仍可能有不可重复读、幻读int ISOLATION_REPEATABLE_READ = 4;       // 可重复读,可以避免脏读、不可重复读,但仍可能有幻读int ISOLATION_SERIALIZABLE = 8;          // 序列化,最高隔离级别,可以避免脏读、不可重复读、幻读int TIMEOUT_DEFAULT = -1;                // 使用默认的超时时间/*** 获取事务的传播行为。** @return 事务传播行为*/default int getPropagationBehavior() {return 0;}/*** 获取事务的隔离级别。** @return 事务隔离级别*/default int getIsolationLevel() {return -1;}/*** 获取事务的超时时间。** @return 事务超时时间*/default int getTimeout() {return -1;}/*** 获取事务是否为只读。** @return 是否为只读事务*/default boolean isReadOnly() {return false;}/*** 获取事务的名称。** @return 事务名称*/@Nullabledefault String getName() {return null;}/*** 创建一个具有默认属性的事务定义。** @return 默认的事务定义*/static TransactionDefinition withDefaults() {return StaticTransactionDefinition.INSTANCE;}
}

通过上面的TransactionDefinition类对应的属性可以看出主要还是对应数据库的事务隔离级别

2.3 Spring事务传播机制

2.3.1 概念

Spring 事务的传播机制说的是,当多个事务同时存在的时候— —⼀般指的是多个事务⽅法相互调⽤
时,Spring 如何处理这些事务的⾏为。
事务传播机制是使⽤简单的 ThreadLocal 实现的,所以,如果调⽤的⽅法是在新线程调⽤的,事务传
播实际上是会失效的。

2.3.2 七种事务传播机制

事务传播机制通过指定@Transactional的propagation属性进行指定

    @Transactional(propagation = Propagation.REQUIRED)public void  test() {}

Propagation枚举里面的7个可选项就是7种对应的传播机制

/*** 定义了事务的传播行为,描述了在多个事务性方法相互调用时,事务的行为方式。*/
public enum Propagation {REQUIRED(0),         // 默认 如果当前没有事务,创建一个新事务;如果已存在事务,则加入到当前事务中SUPPORTS(1),         // 如果当前存在事务,就在事务中执行;否则以非事务方式执行MANDATORY(2),        // 必须在一个已存在的事务中执行,否则抛出异常REQUIRES_NEW(3),     // 每次都创建一个新的事务,挂起当前事务(如果存在)NOT_SUPPORTED(4),    // 以非事务方式执行,挂起当前事务(如果存在)NEVER(5),            // 以非事务方式执行,如果存在事务则抛出异常NESTED(6);           // 如果当前存在事务,就在嵌套事务中执行;否则创建一个新事务private final int value;/*** 构造函数,用于设置传播行为对应的数值。** @param value 传播行为的数值表示*/private Propagation(int value) {this.value = value;}/*** 获取传播行为对应的数值表示。** @return 传播行为的数值表示*/public int value() {return this.value;}
}

2.4 Spring声明式事务实现原理

Spring声明式事务的实现原理是通过 AOP+动态代理进行实现的,主要分为以下两个部分:

2.4.1 Bean初始化创建代理对象

Spring 容器在初始化每个单例 bean 的时候:

  1. 遍历容器中的所有 BeanPostProcessor 实现类,并执⾏其 postProcessAfterInitialization ⽅法
  2. 在执⾏AbstractAutoProxyCreator 类的 postProcessAfterInitialization ⽅法时会遍历容器中所有的切⾯,查找与当前实例化 bean 匹配的切⾯,这⾥会获取事务属性切⾯,查找@Transactional 注解及其属性值,然后根据得到的切⾯创建⼀个代理对象,默认是使⽤ JDK 动态代理创建代理,如果⽬标类是接口,则使⽤ JDK 动态代理,否则使⽤ Cglib。

2.4.2 执行目标方法时进行事务增强

在执⾏⽬标⽅法时进⾏事务增强操作:当通过代理对象调⽤ Bean ⽅法的时候,会触发对应的

  1. AOP 增强拦截器,声明式事务是⼀种环绕增强,对应接⼜为 MethodInterceptor ,事务增强对该
    接⼜的实现为 TransactionInterceptor
  2. 事务拦截器 TransactionInterceptor 在 invoke ⽅法中,通过调⽤⽗类 TransactionAspectSupport
    的 invokeWithinTransaction ⽅法进⾏事务处理,包括开启事务、事务提交、异常回滚。

2.5 Spring声明式事务失效场景

2.5.1 应用在非Public的方法上

Spring AOP 代理时,TransactionInterceptor (事务拦截器)在⽬标⽅法执⾏前后进⾏拦
截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept ⽅法 或
JdkDynamicAopProxy 的 invoke ⽅法会间接调⽤ AbstractFallbackTransactionAttributeSource
的 computeTransactionAttribute⽅法,获取 Transactional 注解的事务配置信息。
在这里插入图片描述

2.5.2 属性propagation设置错误

上面我们讲了7中事务传播行为,如果选择以下三种可能导致事务失效:
在这里插入图片描述

2.5.3 rollbackFor设置错误

  1. rollbackFor 可以指定能够触发事务回滚的异常类型。Spring 默认抛出了未检查 unchecked 异常(继承⾃ RuntimeException 的异常)或者 Error 才回滚事务,其他异常不会触发回滚事务。
  2. 若在⽬标⽅法中抛出的异常是 rollbackFor 指定的异常的⼦类,事务同样会回滚。
// 希 望 ⾃ 定 义 的 异 常可以进⾏回滚
@Transactional( propagation = Propagation .REQUIRED , rollbackFor = MyException . class)

在这里插入图片描述

2.5.4 同一个类中方法调用

是由于使⽤ Spring AOP 代理,只有当事务⽅法被当前类以外的代码调⽤时,才会由 Spring ⽣成的代理对象来管理。如果我们一个类内有两个方法A和方法B。B开启了事务,A调用了B,A没有开启事务,这样的话,B的事务实际是无效的。
例如:

@Service
public class UserService {@Transactionalpublic void processUser(User user) {// Some processing logic}public void createUserAndProcess(String username) {User user = createUser(username);processUser(user);}
}

如果外部调用的是createUserAndProcess的话processUser的事务就会失效。

三:总结提升

本文系统的总结了Spring中事务的不同种类、隔离级别、传播机制、声明式事务实现原理、还总结了4中可能导致事务失效的场景,希望读者读完之后能明确事务的原理,并且在使用中避开常见的可能导致事务失效的坑。

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

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

相关文章

Hlang社区-社区导航栏实现

文章目录 前言项目结构导航实现创作中心移动小球消息提示完整代码前言 okey,这里的话是我们社区导航栏的实现: 废话不多说,看看效果: 我甚至为此用New Bing生成了一个Logo。 项目结构 废话不多说,先来看到我们的项目结构: 在这里导航栏是一个组件。 在App.vue里面直…

【git clone error:no matching key exchange method found】

拉起项目代码报错 git clone ssh://uidxxxgerrit-xxxxxxxx Cloning into ‘xxxxx’… Unable to negotiate with xxx.xx.xxx.ip port xxxxx: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 fatal: Could not …

报道|新鲜出炉!INFORMS公布六位新任期刊主编

推文作者:徐思坤 编者按 INFORMS旗下的六本期刊,Management Science、Operations Research、Service Science、Tutorials in OR、INFORMS Analytics Collection,以及Transportation Science的新任主编公布,并将于2024年1月1日正式…

【内网监控】通过cpolar实现远程监控

【内网监控】通过cpolar实现远程监控 文章目录 【内网监控】通过cpolar实现远程监控前言1. 在cpolar官网预留一个空白隧道2. 完成空白数据隧道,生成地址3. 设置空白隧道的出口4. 空白数据隧道的出口设置5. 获取公网地址6. 打开本地电脑“远程桌面”7. 打开Windows自…

编织梦想:SpringBoot AOP 教程与自定义日志切面完整实战

什么是 AOP AOP 是指通过预编译方式和运行期动态代理的方式,在不修改源代码的情况下对程序进行功能增强的一种技术。AOP 不是面向对象编程(OOP)的替代品,而是 OOP 的补充和扩展。它是一个新的维度,用来表达横切问题&a…

iptables防火墙(SNAT与DNAT)

目录 1 SNAT 1.1 SNAT原理与应用 1.2 SNAT工作原理 1.3 SNAT转换前提条件 2 SNAT示例 ​编辑 2.1 网关服务器配置 2.1.1 网关服务器配置网卡 2.1.2 开启SNAT命令 2.2 内网服务器端配置 2.3 外网服务器端配置 2.4 网卡服务器端添加规则 2.5 SNAT 测试 3 DNAT 3.1 网卡…

Android Framework 全局替换系统字体

基于Android 11 Android Framework 全局替换系统字体 第一种通过替换系统默认字体 将需要替换的字体资源放置frameworks/base/data/fonts/目录下。 将系统默认的Roboto字体替换为HarmonyOs字体。 frameworks/base/data/fonts/fonts.xml frameworks/base/data/fonts/Android.…

隧道HTTP优化程序示例

作为专业爬虫程序员,我们经常需要使用代理服务器处理大量的请求。但是,单一服务器往往无法承担高并发请求和HTTPS加密的压力,这时候我们可以利用CDN来优化性能,并实现反向代理和HTTPS加速。下面,让我们一步步来了解。 …

倒计时动效

1. 效果 2. html <div class"count"><span>3</span><span>2</span><span>1</span> </div>3. css body {width: 100vw;height: 100vh;overflow: hidden;display: flex;justify-content: center;align-items: cente…

利用console提高写bug的效率

前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 自从入坑前端后&#xff0c;日常写bug就没离开过console。 要说用得多&#xff0c;不如说是console.log用得多&#xff0c;console.warn和console.erro…

【高光谱图像的去噪算法】通过全变异最小化对受激拉曼光谱图像进行去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

STM32--ADC模数转换

文章目录 ADC简介逐次逼近型ADCADC框图转换模式数据对齐转换时间校准ADC基本结构ADC单通道工程代码&#xff1a; ADC简介 STM32的ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器&#xff0c;是一种逐次逼近型模拟数字转换器&#xff0c;可以将引脚上连续…

Docker容器:docker基础概述、安装、网络及资源控制

文章目录 一.docker容器概述1.什么是容器2. docker与虚拟机的区别2.1 docker虚拟化产品有哪些及其对比2.2 Docker与虚拟机的区别 3.Docker容器的使用场景4.Docker容器的优点5.Docker 的底层运行原理6.namespace的六项隔离7.Docker核心概念 二.Docker安装 及管理1.安装 Docker1.…

百华劳保|听厂家聊聊如何检测防水劳保鞋?

说起防水劳保鞋大家可能并不陌生&#xff0c;在有积水或水利工程这些工作场景中使用&#xff0c;是防止水渗透鞋子的安全防护鞋。许多企业会为员工发放防水劳保鞋&#xff0c;在采购时一般都需要进行防水测试&#xff0c;提供相对应的检测报告。今天百华小编与大家聊聊都是如何…

vue3+ts-tsconfig.json报错Option ‘importsNotUsedAsValues’

vue3ts-tsconfig.json报错Option ‘importsNotUsedAsValues’ is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption ‘“ignoreDeprecations”: “5.0”’ to silence this error. Use ‘verbatimModuleSyntax’ instead 自我记录 翻译 选项…

OpenGL —— 1、Vs2017搭建glad、glfw环境,并附代码测试

GLFW 简介           GLFW是一个开源的多平台库&#xff0c;用于OpenGL&#xff0c;OpenGL ES和 桌面上的 Vulkan 开发。它提供了一个简单的 API 来创建 窗口、上下文和表面&#xff0c;接收输入和事件。 GLFW是用C语言编写的&#xff0c;支持Windows&#xff0c;mac…

音视频FAQ(三):音画不同步

摘要 本文介绍了音画不同步问题的五个因素&#xff1a;编码和封装阶段、网络传输阶段、播放器中的处理阶段、源内容产生的问题以及转码和编辑。针对这些因素&#xff0c;提出了相应的解决方案&#xff0c;如使用标准化工具、选择强大的传输协议、自适应缓冲等。此外&#xff0…

SpringCloud Gateway:status: 503 error: Service Unavailable

使用SpringCloud Gateway路由请求时&#xff0c;出现如下错误 yml配置如下&#xff1a; 可能的一种原因是&#xff1a;yml配置了gateway.discovery.locator.enabledtrue&#xff0c;此时gateway会使用负载均衡模式路由请求&#xff0c;但是SpringCloud Alibaba删除了Ribbon的…

深度学习入门-3-计算机视觉-卷积神经网络

一、计算机视觉 1.概述 计算机视觉作为一门让机器学会如何去“看”的学科&#xff0c;具体的说&#xff0c;就是让机器去识别摄像机拍摄的图片或视频中的物体&#xff0c;检测出物体所在的位置&#xff0c;并对目标物体进行跟踪&#xff0c;从而理解并描述出图片或视频里的场…

el-table 实现动态表头 静态内容 根据数据显示动态输入框

直接放代码了 <el-table:data"form.tableDataA"borderstripestyle"width: 100%; margin-top: 20px"><el-table-columnv-for"(category, categoryIndex) in form.tableDataA":key"categoryIndex":label"category.name&qu…