【源码】SpringBoot编程式事务使用及执行原理

Spring事务

1、【源码】SpringBoot事务注册原理

2、【源码】Spring Data JPA原理解析之事务注册原理

3、【源码】Spring Data JPA原理解析之事务执行原理

4、【源码】SpringBoot编程式事务使用及执行原理

前言

《Spring事务系列》的前面几篇博文讲解了通过@Transactional注解实现事务的原理,在Spring中,还有另外一种方式可以实现事务,那就是接下去要分享的编程式事务。在开始讲解编程式事务之前,先来看一个编程式事务的例子。

编程式事务示例

在使用编程式事务时,同样需要在项目中添加@EnableTransactionManagement注解,对于SpringBoot项目,默认已经添加了@EnableTransactionManagement注解。示例如下:

@Service
public class MemberStatisticsService {@Resourceprivate TransactionTemplate transactionTemplate;@Resourceprivate MemberStatisticsRepository memberStatisticsRepository;public int addStatistics(MemberStatisticsEntity entity) {// 省略其他// 开启编程式事务boolean rs = transactionTemplate.execute((status) -> {// 省略其他memberStatisticsRepository.save(entity);return true;});return rs ? 1 : 0;}}

使用编程式事务只需两步:

1)引入TransactionTemplate;

2)在需要使用事务的方法中,只需transactionTemplete.execute()方法,在方法内部的回调方法中,添加需要事务保护的数据库相关操作的业务代码即可;

针对以上的示例,可能有人会想,把其中需要事务保护的数据库操作的业务代码剥离为独立的方法,在方法中添加@Transactional注解,在原方法中调用不就可以了嘛。示例如下:

@Service
public class MemberStatisticsService {@Resourceprivate TransactionTemplate transactionTemplate;@Resourceprivate MemberStatisticsRepository memberStatisticsRepository;public int addStatistics(MemberStatisticsEntity entity) {// 省略其他// 开启事务boolean rs = addStatisticsBs(entity);return rs ? 1 : 0;}@Transactionalpublic int addStatisticsBs(MemberStatisticsEntity entity) {// 省略其他memberStatisticsRepository.save(entity);return true;}}

如果这样写,当addStatisticsBs()方法报错的时候,会发现事务并没有回滚,这到底是为何呢?要回答这个问题,还得从源码说起。

事务失效原因

【源码】SpringBoot事务注册原理-CSDN博客

在上面的博客中介绍了方法中添加@Transactional注解时,该类会生成代理类,代理类中添加了TransactionInterceptor拦截器,从而实现了事务管理。

当addStatistics()方法执行时,会先执行ReflectiveMethodInvocation.proceed()方法,循环遍历所有的拦截器。执行完所有拦截器之后,再执行动态代理对象的target类的对应方法,即原方法。详见:

【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码-CSDN博客

博客中的动态代理方法拦截部分。

因为addStatistics()没有添加@Transactional注解,所以执行target的addStatistics()方法,所以在addStatistics()方法内部的this对象是target,而不是代理对象。所以在addStatistics()内部调用addStatisticsBs()方法时,是执行target的addStatisticsBs()方法,所以不再先执行ReflectiveMethodInvocation.proceed(),也就不会执行TransactionInterceptor拦截器,所以没有开启事务管理。

编程式事务的优缺点

3.1 优点

1)灵活性强:开发人员可以在代码中根据具体业务需要来控制事务的范围,特别是大事务或高并发场景,可以缩小事务范围;

2)易于调试:由于事务管理在代码层实现,可以容易的追踪事务管理的细节;

3.2 缺点

1)代码复杂度高:需要手动处理事务,可能增加工作量和代码的复杂度;

2)实现复杂度较高:事务的范围和属性信息需要在代码中显示声明,可能导致一些特定的业务难以满足;

编程事务实现原理

3.1 TransactionTemplate的注入

SpringBoot启动的时候,在TransactionAutoConfiguration配置类中,自动注入TransactionTemplate。代码如下:

public class TransactionAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnSingleCandidate(PlatformTransactionManager.class)public static class TransactionTemplateConfiguration {@Bean@ConditionalOnMissingBean(TransactionOperations.class)public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {return new TransactionTemplate(transactionManager);}}}

在SpringBoot框架中,会自动引入TransactionAutoConfiguration。且在META-INF的spring-autoconfigure-metadata.properties有如下配置:

org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration=
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration=
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration.ConditionalOnSingleCandidate=org.springframework.transaction.PlatformTransactionManager

说明在自动引入TransactionAutoConfiguration时,先引入内部类TransactionAutoConfiguration。

3.2 TransactionTemplate

TransactionTemplate的代码如下:

package org.springframework.transaction.support;@SuppressWarnings("serial")
public class TransactionTemplate extends DefaultTransactionDefinitionimplements TransactionOperations, InitializingBean {/** Logger available to subclasses */protected final Log logger = LogFactory.getLog(getClass());@Nullableprivate PlatformTransactionManager transactionManager;public TransactionTemplate() {}public TransactionTemplate(PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {super(transactionDefinition);this.transactionManager = transactionManager;}public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}@Nullablepublic PlatformTransactionManager getTransactionManager() {return this.transactionManager;}@Overridepublic void afterPropertiesSet() {if (this.transactionManager == null) {throw new IllegalArgumentException("Property 'transactionManager' is required");}}/*** 执行事务*/@Override@Nullablepublic <T> T execute(TransactionCallback<T> action) throws TransactionException {Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);}else {// 获取一个TransactionStatus对象,此实现处理传播行为TransactionStatus status = this.transactionManager.getTransaction(this);T result;try {// 执行入参action的方法,即业务处理方法result = action.doInTransaction(status);}catch (RuntimeException | Error ex) { // 处理RuntimeException和Error的异常,执行回滚,抛对应异常// Transactional code threw application exception -> rollbackrollbackOnException(status, ex);throw ex;}catch (Throwable ex) { // 处理Throwable异常,也会执行回滚,抛UndeclaredThrowableException异常// Transactional code threw unexpected exception -> rollbackrollbackOnException(status, ex);throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");}this.transactionManager.commit(status);return result;}}/*** 执行异常回滚*/private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");logger.debug("Initiating transaction rollback on application exception", ex);try {// 执行回滚this.transactionManager.rollback(status);}catch (TransactionSystemException ex2) {logger.error("Application exception overridden by rollback exception", ex);ex2.initApplicationException(ex);throw ex2;}catch (RuntimeException | Error ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}}@Overridepublic boolean equals(Object other) {return (this == other || (super.equals(other) && (!(other instanceof TransactionTemplate) ||getTransactionManager() == ((TransactionTemplate) other).getTransactionManager())));}}

3.2.1 execute()的核心功能如下:
1)执行TransactionManager.getTransaction(),实际执行AbstractPlatformTransactionManager.getTransaction()方法。根据事务的传播行为,开启事务,获取TransactionStatus对象,详见

【源码】Spring Data JPA原理解析之事务执行原理-CSDN博客

2)调用action.doInTransaction(status),执行入参action的方法,即业务处理方法。业务处理方法传入的参数为TransactionStatus对象;

3)如果业务方法抛RuntimeException或Error类型的异常,则执行回滚,抛对应异常;

4)如果业务方法抛Throwable异常,也执行回滚,抛UndeclaredThrowableException异常;

这是和通过@Transactional注解实现事务时处理的差异,在@Transactional注解实现的事务异常时,默认只处理RuntimeException或Error类型的异常,或者是注解中声明的回滚信息。

5)如果业务方法没有抛异常,则执行TransactionManager.commit()方法;

3.2.2 rollbackOnException()方法中直接调用TransactionManager.rollback(),执行回滚。

业务异常捕获及回滚

通过前面的分析,不管是通过@Transactional注解实现事务还是编程式事务,都是在业务逻辑出现异常时,事务处理会捕获异常,并判断是否要进行事务回滚,然后抛出对应异常。在某些场景中,需要在业务中自己捕获异常,此时就需要使用别的方式实现事务的回滚。

因为业务中自己捕获了异常,所以在事务管理中,会执行事务的提交。事务提交的源码如下:

package org.springframework.transaction.support;@SuppressWarnings("serial")
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {@Overridepublic final void commit(TransactionStatus status) throws TransactionException {if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 如果本地代码设置了回滚if (defStatus.isLocalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Transactional code has requested rollback");}processRollback(defStatus, false);return;}// 全局事务被标记为仅回滚,但事务代码请求提交if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");}processRollback(defStatus, true);return;}// 提交处理processCommit(defStatus);}}

在commit()方法中,在真正提交处理前,会先进行两个判断:

1)defStatus.isLocalRollbackOnly()如果返回true,会执行回滚;

isLocalRollbackOnly()默认返回false,可以通过setRolbackOnly()修改为true。

在TransactionAspectSupport类中,有一个currentTransactionStatus()的静态方法,通过该方法就可以获取到TransactionStatus对象,调用setRollbackOnly()即可。

注:该方法也适用于使用@Transactional注解的方法

2)判断!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly(),如果为true,也会执行回滚;

结尾

本篇就先分享到这里。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

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

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

相关文章

FPGA - 滤波器 - FIR滤波器设计

一&#xff0c;数字滤波器 滤波器是一种用来减少或消除干扰的器件&#xff0c;其功能是对输入信号进行过滤处理得到所需的信号。滤波器最常见的用法是对特定频率的频点或该频点以外的频率信号进行有效滤除&#xff0c;从而实现消除干扰、获取某特定频率信号的功能。一种更广泛的…

如何高效管理和监控 Elasticsearch 别名及索引?

0、引言 在 Elasticsearch 项目中&#xff0c;管理和监控索引是开发者的一项重要任务。 尤其是当我们需要在项目的管理部分展示索引和别名的统计信息时&#xff0c;了解如何有效地列出这些别名和索引显得尤为重要。 本篇博客将介绍几种在 Elasticsearch 中列出别名和索引的方法…

【Hudi】核心概念-基本概念

目录 基本概念时间轴(TimeLine)Instant action:在表上执行的操作类型Instant timeState两个时间概念 文件布局(File Layout)索引(Index)原理索引选项全局索引与非全局索引索引的选择策略 表类型(Table Types)查询类型Snapshot QueriesIncremental QueriesRead Optimized Querie…

集合并集交集差集【工具类】

一、使用Stream流进行处理 在Java开发中&#xff0c;对于集合的操作是非常常见的&#xff0c;特别是交集、并集和差集这样的操作。这些操作可以通过Java 8中的Stream API来实现。 •交集&#xff1a;取两个集合中相同的部分。例如&#xff0c;如果我们有两个List集合&#xff…

【2024最新精简版】Redis面试篇

文章目录 什么是红锁Redis有哪些框架&#xff1f;你们项目中哪里用到了Redis ?Redis的常用数据类型有哪些 ?Redis的数据持久化策略有哪些 ?Redis的数据过期策略有哪些 ?Redis的数据淘汰策略有哪些 ?你们使用Redis是单点还是集群 ? 哪种集群 ?Redis集群有哪些方案, 知道嘛…

iOS--oc对象,类,和元类本质

iOS--oc对象&#xff0c;类&#xff0c;和元类本质 前言实例对象的具体结构自定义类对象的结构继承关系 类信息的存放对isa、superclass总结 前言 最近在学习runtime的过程中&#xff0c;发现其中消息发送-动态方法解析-消息转发中涉及到了大量的类与对象的底层知识&#xff0…

在linux系统上挂载新硬盘

服务器的硬盘空间不够了&#xff0c;自己重新安装了一个硬盘&#xff0c;需要挂载&#xff0c;因为只是用来存放数据&#xff0c;所以不需要分区&#xff0c;直接挂载就可以 #查看当前所有硬盘 sudo fdisk -l #用于显示文件系统的磁盘空间使用情况 df -h发现一个/dev/nvme0n1 …

mysql索引失效的几种情况

1、对列进行计算或者是使用函数&#xff0c;则该列的索引会失效 如&#xff1a;substring(字段名,1,2)‘xxx’&#xff1b; 如&#xff1a;select * from test where id-19;//错误的写法&#xff1b; select * from test where id10; //正确的写法 &#xff1b; 2、某些情况下…

java.nio.charset.UnmappableCharacterException

问题 java.lang.IllegalArgumentException: java.nio.charset.UnmappableCharacterException: Input length 1 解释为编码转换有问题 问题错在位置 非汉字存在 打包的时候就会报异常

TikTok限流封号要如何处理

随着TikTok在全球范围内的运营和管理越来越规范&#xff0c;对于违规行为的处罚也日趋严格。其中&#xff0c;限流和封号是两种常见的处罚措施。那么&#xff0c;当TikTok账号遭遇限流或封号时&#xff0c;我们应该如何处理呢&#xff1f; 一、了解限流和封号的原因 在处理Ti…

Zoom会议网络连接不稳定怎么办?

随着远程办公和在线会议的普及&#xff0c;Zoom已成为许多企业的重要办公工具。然而&#xff0c;国内企业在使用Zoom进行线上会议时&#xff0c;常常面临网络不稳定和中断的问题&#xff0c;这不仅影响会议效率&#xff0c;还可能给企业带来损失。那么&#xff0c;Zoom会议网络…

meilisearch的分页

Elasticsearch 做为老牌搜索引擎&#xff0c;功能基本满足&#xff0c;但复杂&#xff0c;重量级&#xff0c;适合大数据量。 MeiliSearch 设计目标针对数据在 500GB 左右的搜索需求&#xff0c;极快&#xff0c;单文件&#xff0c;超轻量。 所以&#xff0c;对于中小型项目来说…

APP分发平台在推广过程起到什么作用?

APP分发平台在推广过程中起到了至关重要的作用&#xff0c;这些作用主要体现在以下几个方面&#xff1a; 扩大应用覆盖面和市场份额&#xff1a;APP分发平台作为连接开发者和用户的桥梁&#xff0c;通过不同的分发渠道&#xff08;如应用商店、第三方分发平台等&#xff09;&a…

人生感悟 | 我们为什么贫穷?

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 我们为什么贫穷&#xff1f; 因为我们都在局中并坦然的按着规则制定者循规蹈矩的生活。 01 负债导致贫穷 最近同事买房&#xff0c;总价一百多万&#xff0c;月供四千多&#xff0c;讲话&#xff1a;已入坑&#xff0…

python如何终止程序运行

方法1&#xff1a;采用sys.exit(0)&#xff0c;正常终止程序&#xff0c;从图中可以看到&#xff0c;程序终止后shell运行不受影响。 方法2&#xff1a;采用os._exit(0)关闭整个shell&#xff0c;从图中看到&#xff0c;调用sys._exit(0)后整个shell都重启了&#xff08;RESTAR…

SSC30KD SigmaStar 摄像头主控芯片

SSC30KD SigmaStar 摄像头主控芯片

opencv_特征检测和描述

理解特征 寻找独特的特定模式或特定特征&#xff0c;可以轻松跟踪和比较。 拼图&#xff1a;在图像中搜索这些特征&#xff0c;找到它们&#xff0c;在其他图像中查找相同的特征并对齐它们。而已。 基本上&#xff0c;角被认为是图像中的好特征。 在本单元中&#xff0c;我…

【python】如何import 另一个路径下的py文件内容

目录结构&#xff1a; ├─common │ └─config.py └─own_module │ └─run.py问题描述&#xff1a; 如何在run.py 中调用 config.py 中的函数或类&#xff1f; 解决办法&#xff1a; import os import sys # 为了引用自定义模块&#xff0c;可临时将module的绝对路径…

Interview preparation--案例加密后数据的模糊查询

加密数据的模糊查询实现方案 我们知道加密后的数据对模糊查询不是很友好&#xff0c;本篇就针对加密数据模糊查询这个问题来展开讲一讲实现的思路&#xff0c;希望对大家有所启发。为了数据安全我们在开发过程中经常会对重要的数据进行加密存储&#xff0c;常见的有&#xff1…

Python学习从0开始——Kaggle计算机视觉001

Python学习从0开始——Kaggle计算机视觉001 一、卷积分类器1.分类器2.训练分类器3.使用 二、卷积和RELU1.特征提取2.带卷积的过滤器定义3.激活&#xff1a;4.用ReLU检测5.使用 三、最大池化1.最大池压缩2.使用3.平移不变性 四、滑动窗口1.介绍2.步长3.边界4.使用 五、自定义Con…