Spring-事务管理-加强

目录

开启事务

编程式事务

声明式事务

声明式事务的优点

声明式事务的粒度问题

声明式事务用不对容易失效

Spring事务失效可能是哪些原因

@Transactional(rollbackFor = Exception.class)注解

Spring 事务的实现原理

事务传播机制

介绍

用法

rollbackFor

场景举例

事务接口


  • 开启事务

  • 事务管理在系统开发中是不可缺少的一部分,Spring提供了很好的事务管理机制,主要分为编程式事务和声明式事务两种
  • 编程式事务
  • 基于底层的API,如PlatformTransactionManager、TransactionDefinition 和 TransactionTemplate 等核心接口,开发者完全可以通过编程的方式来进行事务管理
  • 编程式事务方式需要开发者在代码中手动的管理事务的开启、提交、回滚等操作

  • 如以上代码,开发者可以通过API自己控制事务
  • 声明式事务
  • 声明式事务管理方法允许在开发者配置的帮助下来管理事务,而不需要依赖底层API进行硬编码
  • 开发者可以只使用注解或基于配置的 XML 来管理事务

  • 如上,使用@Transactional 即可给test方法增加事务控制
  • 声明式事务的优点
  • 通过上面的例子,其实我们可以很容易的看出来,声明式事务帮助我们节省了很多代码,他会自动帮我们进行事务的开启、提交以及回滚等操作,把程序员从事务管理中解放出来
  • 声明式事务管理是使用了 AOP 实现的,本质就是在目标方法执行前后进行拦截
  • 在目标方法执行前加入或创建一个事务,在执行方法执行后,根据实际情况选择提交或是回滚事务
  • 使用这种方式,对代码没有侵入性,方法内只需要写业务逻辑就可以了
  • 但是,声明式事务真的有这么好么?倒也不见得
  • 声明式事务的粒度问题
  • 首先,声明式事务有一个局限,那就是他的最小粒度要作用在方法上
  • 也就是说,如果想要给一部分代码块增加事务的话,那就需要把这个部分代码块单独独立出来作为一个方法
  • 但是,正是因为这个粒度问题,本人并不建议过度的使用声明式事务
  • 首先,因为声明式事务是通过注解的,有些时候还可以通过配置实现,这就会导致一个问题,那就是这个事务有可能被开发者忽略
  • 事务被忽略了有什么问题呢?
  • 首先,如果开发者没有注意到一个方法是被事务嵌套的,那么就可能会在方法中加入一些如RPC远程调用、消息发送、缓存更新、文件写入等操作
  • 我们知道,这些操作如果被包在事务中,有两个问题:
  • 1、这些操作自身是无法回滚的,这就会导致数据的不一致;可能RPC调用成功了,但是本地事务回滚了,可是RPC调用无法回滚了
  • 2、在事务中有远程调用,就会拉长整个事务;那么就会导致本事务的数据库连接一直被占用,那么如果类似操作过多,就会导致数据库连接池耗尽
  • 有些时候,即使没有在事务中进行远程操作,但是有些人还是可能会不经意的进行一些内存操作,如运算
  • 或者如果遇到分库分表的情况,有可能不经意间进行跨库操作
  • 但是如果是编程式事务的话,业务代码中就会清清楚楚看到什么地方开启事务,什么地方提交,什么时候回滚
  • 这样有人改这段代码的时候,就会强制他考虑要加的代码是否应该在方法事务内
  • 有些人可能会说,已经有了声明式事务,但是写代码的人没注意,这能怪谁
  • 话虽然是这么说,但是我们还是希望可以通过一些机制或者规范,降低这些问题发生的概率
  • 比如建议大家使用编程式事务,而不是声明式事务
  • 因为有些时候,声明式事务确实不够明显
  • 声明式事务用不对容易失效
  • 除了事务的粒度问题,还有一个问题那就是声明式事务虽然看上去帮我们简化了很多代码,但是一旦没用对,也很容易导致事务失效
  • 如以下几种场景就可能导致声明式事务失效:
  • 1、@Transactional 应用在非 public 修饰的方法上
  • 2、@Transactional 注解属性 propagation 设置错误
  • 3、@Transactional 注解属性 rollbackFor 设置错误
  • 4、同一个类中方法调用,导致@Transactional 失效
  • 5、异常被catch捕获导致@Transactional 失效
  • 6、数据库引擎不支持事务
  • 以上几个问题,如果使用编程式事务的话,很多都是可以避免的
  • 使用声明式事务失效的问题,如:
  • 因为Spring的事务是基于AOP实现的,但是在代码中,有时候我们会有很多切面,不同的切面可能会来处理不同的事情,多个切面之间可能会有相互影响
  • 在之前的一个项目中,发现Service层的事务全都失效了,一个SQL执行失败后并没有回滚,排查下来才发现,是因为新增了一个切面,这个切面里面做个异常的统一捕获,导致事务的切面没有捕获到异常,导致事务无法回滚
  • 很多人还是会说,说到底还是自己能力不行,对事务理解不透彻,用错了能怪谁
  • 但是还是那句话,我们确实无法保证所有人的能力都很高,也无法要求所有开发者都能不出错
  • 我们能做的就是,尽量可以通过机制或者规范,来避免或者降低这些问题发生的概率
  • 其实,如果大家有认真看过阿里巴巴出的那份Java开发手册的话,其实就能发现,其中的很多规约并不是完完全全容易被人理解,有些也比较生硬,但是其实,这些规范都是从无数个坑里爬出来的开发者们总结出来的
  • 关于@Transactional 的用法,规约中也有提到过,只不过规约中的观点没有这么鲜明

  • Spring事务失效可能是哪些原因
  • Spring中比较容易失效的就是通过@Transactional 定义的声明式事务,他在以下几个场景中会导致事务失效,一定要注意:
  • 1、@Transactional 应用在非 public 修饰的方法上

  • private方法,只会在当前对象中的其他方法中调用,也就是会进行对象的自调用,这种情况是用this调用的,并不会走到代理对象,而@Transactional 是基于动态代理实现的,所以代理会失效
  • 2、@Transactional 注解属性 propagation 设置错误

  • 以上,如果事务发生回滚,则methodA并不会回滚
  • 因为他的propagation是不支持事务,那么他就不会一起回滚
  • 3、@Transactional 注解属性 rollbackFor 设置错误

  • 以上,如果发生非RuntimeException,则事务不会回滚,那么就会导致事务失效
  • 所以需要指定为 (rollbackFor = Exception.class)
  • 4、同一个类中方法调用,导致@Transactional 失效

  • 以上,和private是一回事,因为没办法走到代理服务,所以事务会失效
  • 5、异常被catch捕获导致@Transactional 失效

  • 以为异常被捕获,所以就没办法基于异常进行rollback了,所以事务会失效
  • 6、数据库引擎不支持事务这个好理解,如myisam,不支持的肯定就不行了
  • @Transactional(rollbackFor = Exception.class)注解
  • Exception分为运行时异常RuntimeException和非运行时异常
  • 事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性
  • 当 @Transactional 注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义
  • 如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚
  • 在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事物只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚
  • Spring 事务的实现原理
  • Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring是无法提供事务功能的
  • 真正的数据库层的事务提交和回滚是通过binlog或者 redo log 实现的
  • 编程式事务管理使用 TransactionTemplate
  • 声明式事务管理是建立在 AOP 之上的
  • 其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
  • 声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional 注解的方式,便可以将事务规则应用到业务逻辑中
  • 声明式事务管理要优于编程式事务管理,这正是 Spring 倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持
  • 唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别
  • 事务传播机制

  • 介绍
  • Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为
  • 在复杂的业务场景中,多个事务方法之间的调用可能会导致事务的不一致,如出现数据丢失、重复提交等问题,使用事务传播机制可以避免这些问题的发生,保证事务的一致性和完整性
  • Spring的事务规定了7种事务的传播级别,默认的传播机制是REQUIRED
    • required,如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,总是只有一个事务在执行
    • required_new,每次执行新开一个事务
    • supported,有事务则加入事务,没有事务则普通执行
    • not_supported,有事务则暂停该事务,没有则普通执行
    • mandatory,强制有事务,没有事务则报异常
    • never,有事务则报异常
    • nested,如果之前有事务,则创建嵌套事务,嵌套事务回滚不影响父事务,反之父事务影响嵌套事务
  • Spring的事务隔离有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致
  • 用法
  • 假设有两个业务方法A和B,方法A在方法B中被调用,需要在事务中保证它们的一致性,如果方法A或方法B中的任何一个方法发生异常,则需要回滚事务
  • 使用Spring的事务传播机制,可以在方法A和方法B上使用相同的事务管理器,并通过设置相同的传播行为来保证事务的一致性和完整性
  • 具体实现如下:

  • 在上述示例中,方法A和方法B都使用了REQUIRED的传播行为,表示如果当前存在事务,则在当前事务中执行;
  • 如果当前没有事务,则创建一个新的事务
  • 如果在方法A或方法B中出现异常,则整个事务会自动回滚
  • rollbackFor
  • rollbackFor是Spring事务中的一个属性,用于指定哪些异常会触发事务回滚
  • 在一个事务方法中,如果发生了rollbackFor属性指定的异常或其子类异常,则事务会回滚
  • 如果不指定rollbackFor,则默认情况下只有RuntimeException和Error会触发事务回滚
  • 场景举例
  • 问:一个长的事务方法a,在读写分离的情况下,里面既有读库操作,也有写库操作,再调用个读库方法b,方法b该用什么传播机制呢?
  • 这种情况,读方法如果是最后一步,直接not_supported就行了,避免读报错导致数据回滚
  • 如果是中间步骤,最好还是要required,因为异常失败需要回滚一下
  • 事务接口

  • PlatformTransactionManager:(平台)事务管理器
  • TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
  • TransactionStatus:事务运行状态
  • 所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”
  • PlatformTransactionManager接口介绍
    • Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现
    • Spring事务管理器的接口是:org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了
  • PlatformTransactionManager接口中定义了三个方法:

  • #getTransaction(TransactionDefinition definition)方法,根据事务定义 TransactionDefinition,获得 TransactionStatus
    • 为什么不是创建事务呢?
    • 因为如果当前如果已经有事务,则不会进行创建,一般来说会跟当前线程进行绑定
    • 如果不存在事务,则进行创建
  • #commit(TransactionStatus status)方法,根据 TransactionStatus 情况,提交事务
    • 为什么根据 TransactionStatus 情况,进行提交?
    • 例如说,带@Transactional 注解的的 A方法,会调用 @Transactional 注解的的 B 方法
    • 在 B 方法结束调用后,会执行 PlatformTransactionManager#commit(TransactionStatus status)方法,此处事务是不能、也不会提交的
    • 而是在 A 方法结束调用后,执行 PlatformTransactionManager#commit(TransactionStatus status)方法,提交事务
  • 使用 Spring 事务通过 PlatformTransactionManager,为不同的数据层持久框架提供统一的API,无需关心到底是原生 JDBC、Spring JDBC、JPA、Hibernate 还是 MyBatis

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

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

相关文章

深入探究C++编程中的资源泄漏问题

目录 1、GDI对象泄漏 1.1、何为GDI资源泄漏? 1.2、使用GDIView工具排查GDI对象泄漏 1.3、有时可能需要结合其他方法去排查 1.4、如何保证没有GDI对象泄漏? 2、进程句柄泄漏 2.1、何为进程句柄泄漏? 2.2、创建线程时的线程句柄泄漏 …

CleanMyMacX 永久版下载激活码破解版

CleanMyMac X最新破解版V4.9.2是一款出色的Mac 系统垃圾清理程序。 该软件具有强大的垃圾清理功能,可以释放数 GB 的空间,让您的 Mac 焕然一新。 带有激活码的完整破解版 CleanmyMac 可加速您的 Mac 设备。 此外,cleanmymac还能帮助您从 Mac…

小谈设计模式(17)—状态模式

小谈设计模式(17)—状态模式 专栏介绍专栏地址专栏介绍 状态模式关键角色上下文(Context)抽象状态(State)具体状态(Concrete State) 核心思想Java程序实现首先,我们定义一个抽象状态类 State,其中包含一个处理请求的方法 handleRe…

Centos7 安装mysql 8.0.34并设置不区分大小写

索引 Centos7 安装mysql 8.0.34准备工作安装教程安装并配置配置MySQL配置远程访问重新启动MySQL服务 为已安装的MySQL8设置不区分大小写背景操作步骤 Centos7 安装mysql 8.0.34 准备工作 centos7 服务器 xshell 安装教程 安装并配置 在安装MySQL之前,我们应该…

【无标题】This project has been opened by another efinity instance

This project has been opened by another efinity instance 说明:(1)软件自动即出可能有些进程没有关闭 (2)目录中有中文路径。

POJ 2104 K-th Number 平方分割 / 线段树

一、题目大意 长度为n&#xff08;n<100000&#xff09;的数组&#xff0c;进行m次查询&#xff08;m<5000&#xff09;&#xff0c;每次查询时&#xff0c;输入为 i j k&#xff0c;返回为数组 [i,j] 的分片里第k大数字&#xff08;1<i<j<n,k<j-i1) 二、解…

vue3 中使用echarts图表——柱状图

柱状图是比较常用的图形结构&#xff0c;所以我先收集一些精美的柱状图 一、柱状图&#xff1a;设置圆角和颜色 <template><div class"box" ref"chartDom"></div> </template> <script setup> import { ref, onMounted } fr…

力扣第100题 相同的数 c++ 二叉 简单易懂+注释

题目 100. 相同的树 简单 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出…

RabbitMQ之Direct(直连)Exchange解读

目录 基本介绍 使用场景 springboot代码演示 演示架构 工程概述 RabbitConfig配置类&#xff1a;创建队列及交换机并进行绑定 MessageService业务类&#xff1a;发送消息及接收消息 主启动类RabbitMq01Application&#xff1a;实现ApplicationRunner接口 基本介绍 在r…

功能测试复习

一。测试流程 1.需求评审 确保各部门需求理解一致 2.计划编写 测什么&#xff0c;谁来测&#xff0c;怎么测 3.用例设计 验证项目是否符合需求的操作文档 4.用例执行 项目模块开发完成开始执行用例文档实施测试 5.缺陷管理 对缺陷进行…

[晕事]今天做了件晕事21;设置代理访问网站的时候需注意的问题

今天在家上班&#xff0c;设置好VPN&#xff0c;通过代理来访问公司内部的一个系统浏览器的反应如下&#xff1a; Hmmm… can’t reach this page ***.com refused to connect. 这个返回的错误&#xff0c;非常的具有迷惑性&#xff0c;提示的意思&#xff1a;拒绝链接&#xf…

李沐深度学习记录2:10多层感知机

一.简要知识记录 x.numel()&#xff1a;看向量或矩阵里元素个数 A.sum()&#xff1a;向量或矩阵求和&#xff0c;axis参数可对某维度求和&#xff0c;keepdims参数设置是否保持维度不变 A.cumsum&#xff1a;axis参数设置沿某一维度计算矩阵累计和x*y:向量的按元素乘法 torch.…

时间序列常用数据处理

1.组合技巧Compose 1.2 实例应用及其解释 # 用于组合多个数据处理方法 class Compose(object):def __init__(self, transforms):self.transforms transformsdef __call__(self, seq):for t in self.transforms:seq t(seq)return seq 这段Python代码定义了一个名为Compose的…

【Spring Boot】日志文件

日志文件 一. 日志文件有什么用二. 日志怎么用三. ⾃定义⽇志打印1. 在程序中得到⽇志对象2. 使⽤⽇志对象打印⽇志3. ⽇志格式说明 四. 日志级别1. ⽇志级别有什么⽤2. ⽇志级别的分类与使⽤ 五. 日志持久化六. 更简单的⽇志输出—lombok1. 添加 lombok 依赖2. 输出⽇志3. lom…

Javascript - 轮播图

轮播图也称banner图、广告图、焦点图、滑片。是指在一个模块或者窗口,通过鼠标点击或手指滑动后,可以看到多张图片。这些图片统称为轮播图,这个模块叫做轮播模块。可以通过运用 javascript去实现定时自动转换图片。以下通过一个小Demo演示如何运用Javascript实现。 <!DOCTYP…

《计算机视觉中的多视图几何》笔记(12)

12 Structure Computation 本章讲述如何在已知基本矩阵 F F F和两幅图像中若干对对应点 x ↔ x ′ x \leftrightarrow x x↔x′的情况下计算三维空间点 X X X的位置。 文章目录 12 Structure Computation12.1 Problem statement12.2 Linear triangulation methods12.3 Geomet…

【Java】内部类

目录 概念&#xff1a; 内部类访问特点 示例代码&#xff1a; 运行结果&#xff1a; 内部类分类 1. 成员内部类 示例代码&#xff1a; 2. 静态内部类 示例代码&#xff1a; 3. 方法内部类(局部内部类) 示例代码&#xff1a; 4. 匿名内部类 示例代码&#xff1a; 概…

【开发篇】十七、消息:模拟订单短信通知

文章目录 1、消息2、JMS3、AMQP4、案例&#xff1a;模拟订单短信通知 相关文章&#xff1a; 【同步通讯与异步通讯】 1、消息 消息的发送方&#xff0c;即生产者。消息的接收方&#xff0c;即消费者。同步通信就行打视频&#xff0c;等着对方接电话才能继续往下&#xff0c;而…

文件编码格式

一、问题场景 笔者在写controller层出现了一些小问题&#xff1a;测试controller层的一些请求的时候&#xff0c;后端控制台打印的是乱码&#xff0c;网上找了很多说改UTF-8的&#xff0c;但是我去设置里面全部都改为UTF-8了&#xff0c;结果仍然无济于事&#xff0c;甚至还把…

泊车功能专题介绍 ———— AVP系统基础数据交互内容

文章目录 系统架构系统功能描述云端子系统车辆子系统场端子系统用户APP 工作流程基础数据交互内容AVP 系统基础数据交互服务车/用户 - 云基础数据交互内容车位查询工作流程技术要求数据交互要求 车位预约工作流程技术要求数据交互要求 取消预约工作流程技术要求数据交互要求 泊…