一文带你了解Spring中的事务管理

news2024/12/1 0:24:36

文章目录

  • 前言
  • 一、事务的基础概念
  • 二、spring中事务的使用
    • 声明式事务
    • 编程式事务
    • 如何选择事务方式
  • 三、spring中事务管理实现原理


前言

本文将涉及以下知识点:

  • 事务的基础概念
  • spring当中事务的使用
  • spring当中事务管理的实现原理

一、事务的基础概念

事务(Transaction)是数据库软件中为了保证数据正确的一种手段。
事务必须满足4个特性:

  • 原子性:一个事务操作在软件系统中是不可拆分的,要么执行成功,要么执行失败。事务执行过程中有任何步骤失败,都应该回滚事务
  • 隔离性:事务与事务之间的执行是隔离开的,互相不影响。
  • 持久性:执行成功的事务,数据应该永久保存了,不会丢失。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态。
  • 一致性:事务的一致性是指事务在执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处以一致性状态。比如:张三给李四转钱,不可能张三被扣了钱,李四没有加钱。

二、spring中事务的使用

首先需要明确一点,spring只是帮助我们管理事务操作,省去我们自己去手动执行相应的事务操作sql。真正的事务实现是由数据库提供的。

spring提供了声明式和编程式两种管理事务的方式。两种方式的使用都非常简单。

声明式事务

spring在使用声明式事务前需要先开启事务管理器,否则事务不会生效。在springboot中开启事务配置是用@EnableTransactionManagement注解表示开启。
在这里插入图片描述

声明式事务是在你的逻辑调用入口添加一个 @Transactional注解,以AOP切面的形式帮助我们开启、提交、回滚事务。
在这里插入图片描述
声明式事务最大的优点就是使用简单、基本上对业务逻辑代码无侵入。开发者只需要关注业务逻辑,而不必关心事务的处理。缺点则是在使用@Transactional注解时,有一些会导致事务失效的点,需要特别注意
在这里插入图片描述
事务失效场景参考


编程式事务

编程式事务通常是由TransactionTemplate类来操作的,这个类是spring官方提供的操作事务的模板类,类路径为:

org.springframework.transaction.support.TransactionTemplate

TransactionTemplate通过执行execute(params)方法,接收一个TransactionCallback类型的参数。实现的事务管理。使用示例如下:
在这里插入图片描述
使用编程式事务需要注意,所有的sql操作需要写在doInTransaction()方法体内,否则你的sql执行不会被纳入到事务操作,将不具体事务性。

如何选择事务方式

首先,声明式事务、编程式事务二者在底层实现上是一样的。唯一不同的只有使用方面。具体两种方式如何选择的话。

建议业务逻辑复杂情况使用编程式事务,它可以更好的把控业务处理时事务的粗细粒度。如果业务不是很复杂的场景,可以使用声明式事务处理。使用的时候注意一下上面提到的会导致事务失效的点。还有一个点,spring中事务是跟线程绑定的,事务只能控制当前线程的操作。


三、spring中事务管理实现原理

从源码层次带大家看一下spring是怎么帮助我们管理事务的。

从编程式事务的角度出发来看原理的话,非常方便。直接阅读TransactionTemplate模板类的源码即可。

 @Nullable
    public <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 status = this.transactionManager.getTransaction(this);

            Object result;
            try {
                //执行业务逻辑,上面说到的sql语句都要放到doInTransaction()方法体内执行,就是在这里调用了
                result = action.doInTransaction(status);
            } catch (Error | RuntimeException var5) {
                //如果业务逻辑执行有异常,捕获到了直接回滚事务
                this.rollbackOnException(status, var5);
                throw var5;
            } catch (Throwable var6) {
                //同上
                this.rollbackOnException(status, var6);
                throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
            }
            
            //业务执行完成,commit提交事务
            this.transactionManager.commit(status);
            return result;
        }
    }

上面TransactionTemplate.execute()方法中完整了罗列了事务管理的整个周期,开启、提交/回滚 。

下面从声明式事务角度来分析一下源码实现,由于声明式事务在使用上只有一个 @Transactional注解,所以需要找到事务aop的切面逻辑才能看到底层实现。由于声明式事务需要开启@EnableTransactionManagement,所以我们从@EnableTransactionManagement注解开始分析。
在这里插入图片描述
通过import导入配置类,进来找到这个类:ProxyTransactionManagementConfiguration
在这里插入图片描述

切入点在AnnotationTransactionAttributeSource类里
在这里插入图片描述
添加解析器,用来匹配查询@Transactional注解
在这里插入图片描述
AnnotationTransactionAttributeSource.isCandidateClass()方法会匹配查找@Transactional切入点。
在这里插入图片描述

具体的切入匹配是在SpringTransactionAnnotationParser.isCandidateClass()方法下面
在这里插入图片描述
成功匹配到@Transactional注解。接下来就是具体的切面逻辑执行。

切面逻辑在TransactionInterceptor类里(为什么切面逻辑在TransactionInterceptor类里,看下spring aop 内部实现)
在这里插入图片描述
进入TransactionInterceptor.invoke()方法。
在这里插入图片描述
为什么看invoke方法?实现了MethodInterceptor接口、看下TransactionInterceptor类的继承关系。
在这里插入图片描述
继续跟invoke方法,来到TransactionAspectSupport类下的invokeWithinTransaction方法

 @Nullable
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
        TransactionAttributeSource tas = this.getTransactionAttributeSource();
        TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
        TransactionManager tm = this.determineTransactionManager(txAttr);
        if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
            TransactionAspectSupport.ReactiveTransactionSupport txSupport = (TransactionAspectSupport.ReactiveTransactionSupport)this.transactionSupportCache.computeIfAbsent(method, (key) -> {
                if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && TransactionAspectSupport.KotlinDelegate.isSuspend(method)) {
                    throw new TransactionUsageException("Unsupported annotated transaction on suspending function detected: " + method + ". Use TransactionalOperator.transactional extensions instead.");
                } else {
                    ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
                    if (adapter == null) {
                        throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " + method.getReturnType());
                    } else {
                        return new TransactionAspectSupport.ReactiveTransactionSupport(adapter);
                    }
                }
            });
            return txSupport.invokeWithinTransaction(method, targetClass, invocation, txAttr, (ReactiveTransactionManager)tm);
        } else {
            PlatformTransactionManager ptm = this.asPlatformTransactionManager(tm);
            String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
            if (txAttr != null && ptm instanceof CallbackPreferringPlatformTransactionManager) {
                TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder();

                Object result;
                try {
                    result = ((CallbackPreferringPlatformTransactionManager)ptm).execute(txAttr, (statusx) -> {
                        TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(ptm, txAttr, joinpointIdentification, statusx);

                        Object var9;
                        try {
                            Object retVal = invocation.proceedWithInvocation();
                            if (retVal != null && vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {
                                retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, statusx);
                            }

                            var9 = retVal;
                            return var9;
                        } catch (Throwable var13) {
                            if (txAttr.rollbackOn(var13)) {
                                if (var13 instanceof RuntimeException) {
                                    throw (RuntimeException)var13;
                                }

                                throw new TransactionAspectSupport.ThrowableHolderException(var13);
                            }

                            throwableHolder.throwable = var13;
                            var9 = null;
                        } finally {
                            this.cleanupTransactionInfo(txInfo);
                        }

                        return var9;
                    });
                } catch (TransactionAspectSupport.ThrowableHolderException var20) {
                    throw var20.getCause();
                } catch (TransactionSystemException var21) {
                    if (throwableHolder.throwable != null) {
                        this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                        var21.initApplicationException(throwableHolder.throwable);
                    }

                    throw var21;
                } catch (Throwable var22) {
                    if (throwableHolder.throwable != null) {
                        this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                    }

                    throw var22;
                }

                if (throwableHolder.throwable != null) {
                    throw throwableHolder.throwable;
                } else {
                    return result;
                }
            } else {
            //主要逻辑在这,默认走的是这段代码块,开启事务
                TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

                Object retVal;
                try {
                //执行业务逻辑
                    retVal = invocation.proceedWithInvocation();
                } catch (Throwable var18) {
                //异常回滚事务
                    this.completeTransactionAfterThrowing(txInfo, var18);
                    throw var18;
                } finally {
                    this.cleanupTransactionInfo(txInfo);
                }

                if (retVal != null && vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {
                    TransactionStatus status = txInfo.getTransactionStatus();
                    if (status != null && txAttr != null) {
                        retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                    }
                }
                //业务执行完成,commit提交事务
                this.commitTransactionAfterReturning(txInfo);
                return retVal;
            }
        }
    }

到大致讲一下:spring在帮我们开启事务的时候是通过设置 自动提交autocommit=0 开启一条事务,提交/回滚的时候都是通过JDBC DataSource获取数据库连接,执行commit/rollback 命令完成的。大家可以按照上面代码标注的注释点一层一层点进去查看。

底层跟踪下去,声明式以及编程式事务都是由AbstractPlatformTransactionManager默认的事务管理器处理。

开启事务处理链路:AbstractPlatformTransactionManager.getTransaction() => AbstractPlatformTransactionManager.startTransaction() => AbstractPlatformTransactionManager.doBegin() => DataSourceTransactionManager.doBegin()
在这里插入图片描述

提交事务处理链路:AbstractPlatformTransactionManager.doCommit() => DataSourceTransactionManager.doCommit()

 protected void doCommit(DefaultTransactionStatus status) {
        DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
        //获取数据库连接
        Connection con = txObject.getConnectionHolder().getConnection();
        if (status.isDebug()) {
            this.logger.debug("Committing JDBC transaction on Connection [" + con + "]");
        }

        try {
            //根据具体数据库驱动实现,执行不同提交事务语法
            con.commit();
        } catch (SQLException var5) {
            throw new TransactionSystemException("Could not commit JDBC transaction", var5);
        }
    }

以mysql举例,底层会执行commit命令:con.commit() => com.mysql.cj.jdbc.ConnectionImpl.commit()
在这里插入图片描述
回滚事务同理:AbstractPlatformTransactionManager.doCommit() => DataSourceTransactionManager.rollback()
在这里插入图片描述

在mysql驱动下的实现:
con.rollback() => com.mysql.cj.jdbc.ConnectionImpl.rollback()
com.mysql.cj.jdbc.ConnectionImpl.rollback() => com.mysql.cj.jdbc.ConnectionImpl.rollbackNoChecks()
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

python画图

python画图1.使用matplotlib画图2.使用pyecharts画图2.x pyecharts的三种图片渲染工具2.x.1 snapshot_selenium2.x.2 snapshot_phantomjs2.x.3 snapshot_pyppeteer1.使用matplotlib画图 2.使用pyecharts画图 pyecharts是一款将python与echarts结合的强大的数据可视化工具&…

阿里云服务器安装tomcat

一、前置条件 安装tomcat需要先安装jdk&#xff0c;所以没有安装jdk同学&#xff0c;详见参考文章或者此文章 二、Linux上安装tomcat 1. 下载Apache tomcat tomcat官网下载地址 在左边&#xff0c;可以选择下载各种版本的tomcat。根据服务器操作系统选择下载。Linux操作系统…

第七章TCP/IP——ARP网络攻击与欺骗

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。个人爱好: 编程&#xff0c;打篮球&#xff0c;计算机知识个人名言&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石…

分布式文件系统和对象存储魔力象限,右上角都有谁?

自Gartner 首次发布      分布式文件系统和      对象存储魔力象限以来      戴尔科技集团      就牢牢位居领导者象限      今年也不例外      恭喜      连续第七年获评领导者!    对于入选本年度的魔力象限领导者,我们感到十分荣幸。我们相…

docker安装jenkins最新版

前言 使用的是centos7的linux系统&#xff0c; 检查docker 是否开启网络 如果没有开启网络会报错:WARNING: IPv4 forwarding is disabled. Networking will not work. 检查网络状态 sysctl net.ipv4.ip_forward如果返回为“net.ipv4.ip_forward 1”则表示网络转发正常&am…

1534_TriCore编译器Tasking使用_汇编语言语法以及标识符

全部学习汇总&#xff1a; GreyZhang/TriCore_Tasking_Compiler_Skills: Some skills for Tasking compiler on AURIX platform. Happy hacking! (github.com) 1. 如同C语言&#xff0c;汇编语言也可以续行而且采用了同样的续行符号。不过&#xff0c;我遇到的汇编一直都是很统…

TMS Echo数据复制的Delphi框架

TMS Echo数据复制的Delphi框架 TMS Echo是用于数据复制的Delphi框架。它是TMS Business产品阵容的一部分&#xff0c;它取决于TMS Aurelius的运营。 TMS Echo允许您至少拥有两个数据库并在它们之间同步信息。您对单个客户数据库所做的更改(插入、更新、删除)可能会传输到其他数…

PowerMax——业界首个引入NVIDIA BlueField DPU的高端存储

高性能、可扩展、弹性与空间 全球越来越多的领先企业 都依靠关键任务型存储 承载企业核心业务 DPU作为一个新兴起的概念,正在以惊人的速度崛起。据预测,DPU将成为未来计算的三大支柱之一,CPUGPUDPU构成未来的数据中心。 现在,全球首款 集成NVIDIA BlueField DPU技术的 关…

QMudiaPlayer(状态)

在音乐播放中介绍了QMediaPlayer的基本用法&#xff0c;在这里更深度的使用QMediaPlayer。 媒体播放器的当前媒体的状态&#xff1a; QMediaPalyer&#xff1a;&#xff1a;MediaStatus 会发射 mediaStatusChanged&#xff08;&#xff09;信号 QMediaPlayer::UnknownMediaSt…

数据库、计算机网络,操作系统刷题笔记3

数据库、计算机网络&#xff0c;操作系统刷题笔记3 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&…

精度和召回率的区别

先弄懂TP、FP、FN、TN T:ture 这件事是做对了 F:false 这件事做错了 N:negtive 负类 P:positive 正类TP:正确的把它判断成正例了 FP&#xff1a;错误的把它判断成正例了 FN&#xff1a;错误的把它判断成负例了 它本身是正例&#xff0c;把它判断成负例&#xff0c;为0个 TN…

[附源码]Python计算机毕业设计电影院订票系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

grafana 画富集多个指标 label 的表格

下午5点&#xff1a;老哥&#xff0c;今天把业务趋势图搞出来吧。 一、怎么画表格 我们的需要是做下面的视图&#xff0c;他是一个表格&#xff0c;而且有着多个数据源 添加图表 填入数据&#xff0c;展示原始图形 转换为 table 只显示最新一条 选择查询条件的 instant…

【前端】CSS

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录前言一、【基本语法规范】二、【CSS引入方式】三、【代码风格】四、【CSS选择器】1. 基础选择器2. 复合选择器五、【CSS常用元素属性】1. 【字体相关属性】2. 【文本属性】3. 【背景属性】4. 【圆角矩形】5. 【调试】…

C++回顾从入门开始

前言 新手搭配视频 回顾复习直接看 有问题请评论提出看到会及时修改 include、iostream、main() #include <iostream> using namespace std; int main() {cout << "Hello World!" << endl;return 0; } 解释 int表示函数的返回值类型&#xff0c…

GAMES202 Real-time Environment Mapping

文章目录前言Lecture5 Real-time Environment Mapping (prefiltering&#xff0c;split sum)补充&#xff1a;反射波瓣&#xff08;Lobe&#xff09;Lecture6 Real-time Environment Mapping (precomputed radiance transfer)Spherical Harmonicsprecomputed radiance transfer…

基于VggNet网络与ResNet神经网络的物体分类识别研究-附Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、VGGNet网络与ResNet网络模型✳️ 2.1 VGG16 网络✳️ 2.2 ResNet-18网络✳️ 三、训练与实验结果✳️ 3.1 CIFA&#xff32;-10 数据集✳️ 3.2 ResNet-18训练与识别结果✳️ 3.3 VGG16 网络训练与识别结果✳️ 四、参考文献✳️ 五、Mat…

使用vue脚手架快速搭建vue 2项目

简单了解vue-cli 官网地址:https://cli.vuejs.org/zh/guide/browser-compatibility.html 前提 1.安装node(js代码的运行环境)、npm&#xff1b; 2.全局安装vue-cli; 命令创建项目 vue create hello-word 等待项目创建成功即可 开发工具打开刚刚创建的项目 项目结构如图…

RS232和RS485

1. 串口存在的问题 电器接口不统一。UART只是对信号的时序和格式进行定义&#xff0c;未定义接口的电气特性 Uart通信一般直接使用从处理器的TTL电平&#xff0c;但不同的处理器使用的电平存在差异&#xff0c;导致不通处理器一般不能使用UART相互连接 UART没有规定连接器件的连…

Css 3 动画

动画&#xff08;animation&#xff09;是css3中具有颠覆性的特征之一&#xff0c;可通过设置多个节点来精确控制一个或一组动画&#xff0c;常用来实现复杂的动画效果。 相比较过渡&#xff0c;动画可以实现更多变化&#xff0c;更多控制&#xff0c;连续自动播放等效果。 动…