# Spring 事务失效场景
Spring 事务失效场景
文章目录
- Spring 事务失效场景
- 前言
- 事务不生效
- 未开启事务
- 事务方法未被Spring管理
- 访问权限问题
- 基于接口的代理
- 源码解读
- CGLIB代理
- 方法用final修饰
- 同一类中的方法调用
- 多线程调用
- 不支持事务
- 事务不回滚
- 设置错误的事务传播机制
- 捕获了异常
- 手动抛了别的异常
- 自定义了回滚异常
- 事务被手动提交
- 其它
- 大事务问题
- 缩小事务范围
- 手动提交事务
- 异步处理
- 事务的性能和并发性
前言
# Spring事务详解
Spring
事务是用于解决数据库操作中的一致性和隔离性问题的机制。数据库事务是一组操作,要么全部成功执行,要么全部回滚,以确保数据的完整性和一致性。Spring
事务管理的主要目的是确保在多个数据库操作中,要么所有操作都成功提交,要么所有操作都回滚,从而保持数据的一致性。它提供了以下几个方面的解决方案:
-
原子性(
Atomicity
):事务要么全部成功执行,要么全部回滚,确保数据库操作的原子性。 -
一致性(
Consistency
):事务在执行前后,数据库的状态应保持一致。如果事务执行失败,数据库应该回滚到事务开始之前的状态。 -
隔离性(
Isolation
):事务应该在相互之间隔离,以避免并发操作引起的问题。它确保了在并发环境下,每个事务都能够独立地执行,并且不会相互干扰。 -
持久性(
Durability
):一旦事务提交,其结果应该持久保存在数据库中,即使发生系统故障或重启。
Spring
事务管理通过使用注解或编程方式来定义事务边界,它可以应用于各种数据访问技术(如JDBC
、Hibernate
、JPA
等)。它还提供了不同的传播行为和隔离级别,以满足不同的业务需求。- 通过使用
Spring
事务,开发人员可以简化数据库操作的管理,并确保数据的一致性和可靠性,从而提高应用程序的可靠性和性能。
事务不生效
未开启事务
- 如果使用的是
springboot
项目,springboot
通过DataSourceTransactionManagerAutoConfiguration
类,默认开启了事务。只需要配置spring.datasource
相关参数即可。
- 如果使用的是传统的
spring
项目,则需要在applicationContext.xml
文件中,手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。
事务方法未被Spring管理
- 未将类标记为
Spring
管理的组件:确保类被标记为@Service
、@Component
或其他适当的注解,以便Spring
能够扫描并管理该类。 - 未使用
@Transactional
注解标记事务方法:确保需要进行事务管理的方法被标记为@Transactional
注解,以便Spring
能够识别并应用事务管理。
访问权限问题
基于接口的代理
- 当使用基于代理的事务管理时,
Spring
会在运行时生成一个代理对象来管理事务。这个代理对象会拦截被注解的方法,并在方法执行前后进行事务的开启、提交或回滚等操作。 - 默认情况下,
Spring
的事务代理是基于接口实现的,因此只有public
方法才能被代理。如果事务方法是private
、protected
或默认访问级别的,Spring
无法生成代理对象,从而导致事务不生效。
/*** 不生效*/
@Transactional
private void ransactionOne() {User user = new User();user.setUsername("张三");userMapper.insertUser(user);methodOne();int a = 3 / 0;logger.info(String.valueOf(a));
}
源码解读
-
判断是否是
public
的
-
有事务的类
CGLIB代理
- 如果使用基于类的代理,即使用
CGLIB
代理,Spring
可以代理非public
方法。可以通过配置proxy-target-class
属性为true
来启用基于类的代理。 - 如果使用的是基于类的代理,事务方法可以是非
public
的,但需要启用基于类的代理。 SpringBoot
事务示例:通过在@EnableTransactionManagement
注解上设置proxyTargetClass
属性为true
来启用基于类的代理。
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
public class AppConfig {// 配置其他的Bean和组件
}
- 使用基于类的代理时,非
public
方法也可以被代理。但需要注意,启用基于类的代理可能会带来一些性能开销,因此只有在确实需要代理非public
方法时才应使用。 - 确保在配置类上添加了
@EnableTransactionManagement
注解,并根据需要设置proxyTargetClass
属性,就可以在Spring Boot
中使用基于类的代理来进行事务管理了。 Spring
使用CGLIB
库的Enhancer
类来生成代理对象,生成的代理对象中包含EnhancerBySpringCGLIB
作为标识符
方法用final修饰
- 在
Spring
中,事务是通过动态代理来实现的。当一个类被代理时,Spring
会创建一个代理对象来包装原始对象,从而在方法调用前后添加事务处理逻辑。然而,对于final
方法,由于无法重写,因此无法创建代理对象,事务管理器也就无法对其进行事务处理。
@Service
public class UserService {@Transactionalpublic final void add(UserModel userModel){saveData(userModel);updateData(userModel);}
}
同一类中的方法调用
this
是被真实对象,所以会直接走methodTwo
的业务逻辑,而不会走切面逻辑,所以事务失败。
/*** 同一个类中的方法调用*/
@Override
public void transactionSix() {User user = new User();user.setUsername("张三");userMapper.insertUser(user);// 没有事务的方法调用有事务的方式,相当于使用 this 调用不会走 AOP 的逻辑methodTwo();
}@Transactional(rollbackFor = Throwable.class)
public void methodTwo() {User user = new User();user.setUsername("李四");userMapper.insertUser(user);int a = 5 / 0;logger.info(String.valueOf(a));
}
- 解决方法可以是在方法上添加
@Transactional
注解 @EnableAspectJAutoProxy(exposeProxy = true)
在启动类中添加,会由Cglib
代理实现。- 如果只想让
methodTwo
的事务生效,可以把methodTwo
写到一个新的service
,用service
调用
/*** 同一个类中的方法调用,调用 service 的方法,insetUser 方法事务可以生效*/
@Override
public void transactionSeven(){User user = new User();user.setUsername("张三");userMapper.insertUser(user);transactionHelpService.insetUser();
}@Transactional(rollbackFor = Throwable.class)
@Override
public void insetUser() {User user = new User();user.setUsername("李四");userMapper.insertUser(user);int a = 5 / 0;logger.info(String.valueOf(a));
}
多线程调用
- 同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
不支持事务
-
数据库本身无法支持事务的场景下,例如使用
Mysql
的MyISAM
引擎 -
在
MySQL5.5
往后,默认采用InnoDB存储引擎
,在这之前采用MyISAM存储引擎
。 -
InnoDB
是MySQL
的默认事务型引擎
,它被设计用来处理大量的短期(short-lived)事务
。可以确保事务的完整提交(Commit)和回滚(Rollback)
。 -
MyISAM
提供了大量的特性,包括全文索引、压缩、空间函数(GIS)
等,但MyISAM
不支持事务、行级锁、外键
,有一个毫无疑问的缺陷就是崩溃后无法安全恢复
。
事务不回滚
设置错误的事务传播机制
Propagation.NEVER
:这种类型的传播特性不支持事务,如果有事务则会抛异常。- 目前只有这三种传播特性才会创建新事务:
REQUIRED
,REQUIRES_NEW
,NESTED
捕获了异常
- 在代码中手动
try...catch
了异常
@Transactional
@Override
public void testOne() {try {User user = new User();user.setUsername("张三");userMapper.insertUser(user);int a = 3 / 0;} catch (Exception e) {logger.error(e.getMessage(), e);}
}
- 在
Spring Boot
中,事务是通过AOP
(面向切面编程)机制实现的。当使用@Transactional
注解标记一个方法时,Spring
会在方法开始前创建一个事务,并在方法执行结束后根据方法的执行结果来决定是否提交或回滚事务。 - 如果在方法执行期间发生异常,
Spring
会捕获该异常并将事务标记为“回滚”。这意味着在方法执行结束后,Spring
会自动回滚该事务并撤销对数据库的任何更改。 - 如果在方法中捕获了异常并处理了它,那么
Spring
就无法感知到该异常,并且不会将事务标记为“回滚”。这意味着在方法执行结束后,Spring
会将事务提交,而不是回滚,这可能会导致不一致的数据状态。
手动抛了别的异常
spring
事务,默认情况下只会回滚RuntimeException
(运行时异常)和Error
(错误),对于普通的Exception
(非运行时异常),它不会回滚。- 如果在执行方法时抛出了
RuntimeException
类型的异常,Spring
会认为这个异常是不可恢复的,也就是说无法通过异常处理来修复。在这种情况下,Spring
会回滚事务,撤销之前的所有数据库操作,以保证数据的一致性。 - 如果抛出的是非
RuntimeException
类型的异常,Spring
会认为这个异常是可以恢复的,意味着可以通过异常处理来修复。在这种情况下,Spring
不会回滚事务,而是允许应用程序继续执行。 - 这个机制的主要目的是保护数据的一致性。在大多数情况下,如果发生了非
RuntimeException
类型的异常,应用程序可能会尝试通过其他方式来处理异常并继续执行。而如果发生了RuntimeException
类型的异常,应用程序可能无法继续执行,因此需要回滚事务以撤销之前的数据库操作。
/*** 在代码中手动抛出别的异常 事务不回滚*/
@Override
public void testTwo() throws Exception {try {User user = new User();user.setUsername("张三");userMapper.insertUser(user);int a = 3 / 0;} catch (Exception e) {logger.error(e.getMessage(), e);throw new Exception("test");}
}
自定义了回滚异常
-
在使用
@Transactional
注解声明事务时,有时我们想自定义回滚的异常,spring
也是支持的。可以通过设置rollbackFor
参数 -
rollbackFor
参数指定了回滚事务异常的类型,需要检查抛出的异常是否是指定的异常,如果不一致则回滚不了
-
开发规范中会提示需要指定
rollbackFor
参数,事务默认只会在捕获到未被处理的RuntimeException
或 Error 时才会回滚。如果你的代码中捕获了异常并进行了处理,但没有再次抛出RuntimeException
或 Error,事务将不会回滚。确保在捕获异常时,将异常重新抛出或手动触发回滚。
事务被手动提交
- 如果在代码中手动调用了
commit()
方法来提交事务,而没有调用rollback()
方法来回滚事务,事务将不会回滚。确保在需要回滚的情况下,正确地调用了回滚方法。
其它
大事务问题
缩小事务范围
- 将大事务拆分为多个小事务,并使用嵌套事务来管理它们
手动提交事务
- 如果需要更细粒度的控制,可以在代码中手动管理事务。使用
TransactionTemplate
类可以手动启动、提交或回滚事务
异步处理
- 对于非关键的操作,你可以考虑使用异步处理来减少事务的时间。
- 将一些长时间运行的操作放在异步任务中,这样可以减少事务的持有时间,从而减少事务的大小和影响范围。
事务的性能和并发性
- 大事务可能会导致性能问题和并发性竞争。在设计事务时,要考虑事务的持有时间和锁竞争情况。尽量将事务范围缩小到最小,避免长时间持有事务锁,以提高性能和并发性。
相关文章:

# Spring 事务失效场景
Spring 事务失效场景 文章目录 Spring 事务失效场景前言事务不生效未开启事务事务方法未被Spring管理访问权限问题基于接口的代理源码解读 CGLIB代理 方法用final修饰同一类中的方法调用多线程调用不支持事务 事务不回滚设置错误的事务传播机制捕获了异常手动抛了别的异常自定义…...
华为OD 停车场车辆统计(100分)【java】A卷+B卷
华为OD统一考试A卷+B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应20022部分考题以及新出的题目,A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载,请点击以下链接进入: 我用夸克网盘分享了「华为O…...

出差学小白知识No6:LD_PRELOAD变量路径不对找不到库文件
交叉编译的时候出现以下问题,显示LD_PRELOAD变量找不到路劲 首先先查看一下LD_PRELOAD的路径:echo $LD_PRELOAD 如果输出一大串,那么先进行清空:unset LD_PRELOAD 重新给LD_PRELOAD进行赋值他的路径和库文件: expor…...

利用dns协议发起ddos反射攻击
利用DNS服务器发起反射型DDOS,攻击带宽 基本思路: 1、利用any类型的dns查询,可完成发送少量请求数据,获得大量返回数据。 2、将原请求地址改为受害者地址,则dns会向受害者返回大量数据,占用带宽 警告&…...

Tcl基础知识
一、概述 Tcl 语言的全称 Tool Command Language,即工具命令语言。这种需要在 EDA 工具中使用的相当之多,或者说几乎每个 EDA 工具都支持 Tcl 语言,并将它作为自己的命令shell。 静态时序分析中多用的 Synopsys Tcl 语言,…...
Go中的编程模式:Pipeline
本文章我们重点来介绍一下 Go 编程中的 Pipeline 模式。用过 Linux 命令行的人都不会陌生,它是一种把各种命令拼接起来完成一个更强功能的技术方法,在C语言中也有pipe管道的叫法,具体的有兴趣的同学也可以去了解。 现在的流式处理、函数式编程、应用网关对微服务进行简单的…...

2023最新pytorch安装教程,简单易懂,面向初学者(Anaconda+GPU)
一、前言 目前是2023.1.27,鉴于本人安装过程中踩得坑,安装之前我先给即将安装pytorch的各位提个醒,有以下几点需要注意 1.判断自己电脑是否有GPU 注意这点很重要,本教程面向有NVIDA显卡的电脑,如果你的电脑没有GPU或者使用AMD显…...

Redis为什么变慢了
一、Redis为什么变慢了 1.Redis真的变慢了吗? 对 Redis 进行基准性能测试 例如,我的机器配置比较低,当延迟为 2ms 时,我就认为 Redis 变慢了,但是如果你的硬件配置比较高,那么在你的运行环境下,可能延迟是 0.5ms 时就可以认为 Redis 变慢了。 所以,你只有了解了你的…...

空中计算(Over-the-Air Computation)学习笔记
文章目录 写在前面 写在前面 本文是论文A Survey on Over-the-Air Computation的阅读笔记: 通信和计算通常被视为独立的任务。 从工程的角度来看,这种方法是非常有效的,因为可以执行孤立的优化。 然而,对于许多面向计算的应用程序…...
如何高效率地阅读论文
▚ 01 Active versus passive reading: how to read scientific papers? 📢小疑则小悟,大疑则大悟,不疑则不悟。 If you read/do research with small questions in mind, you learn small things. If you do so with big questions in…...

FreeRTOS学习day1
顾名思义 免费的实时操作系统 用法基本和Linux下的多线程编程类似 探索者开发版实验 动态创建4个任务start_task task1 task2 task3 优先级依次为1 2 3 4 (注意优先级不能为0,0是空闲任务) 我的理解:主线程start_task 主线程 task1 ta…...

【Web】| CSS Float (浮动)的使用方法
Float(浮动)概念 CSS的Float(浮动),会使得元素向左或者向右移动,其它周围元素也会重新排列。 Float浮动,往往是用于图像,但它的布局一样非常有效。 元素如何浮动 元素的水平方向…...
#力扣:面试题 02.03. 删除中间节点@FDDLC
面试题 02.03. 删除中间节点 - 力扣(LeetCode) 一、Java /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val x; }* }*/ class Solution {public void deleteNode(List…...
多线程原子性、一致性与有序性
作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有需要我的支持,请私信或评论留言! 前言: …...
读书笔记:Effective C++ 2.0 版,条款28(namespace )
条款28: 划分全局名字空间 namespace 作为前缀,防止不同名字域的类型、常量等互相污染。 没命名的名字空间一般用于限制名字空间内部元素的可见性。 namespace sdm {const double book_version 2.0;class handle { ... };handle& gethandle(); }早期用struct模…...

CSS常见选择器总结
1.简单选择器 简单选择器是开发中使用最多的选择器,包含: 元素选择器,使用元素的名称 类选择器,使用.类名 id选择器,使用#id id注意事项: 一个HTML文档里面的id值 是唯一的,不能重复 id值如…...

阿里云服务结构--长期更新
CNCF 全称Cloud Native Computing Foundation(云原生计算基金会),成立于 2015 年7月21日(于美国波特兰OSCON 2015上宣布),其最初的口号是坚持和整合开源技术来让编排容器作为微服务架构的一部分࿰…...

Java8 BiConsumer<T, U> 函数接口浅析分享(含示例,来戳!)
文章目录 Java8 BiConsumer<T, U> 函数接口浅析分享(含示例,来戳!)源码accept 方法示例示例一示例二 andThen 方法示例示例一示例二 示例相关代码类dohandler 方法student.javaStudentScore.javaStudentScoreDto.java Java8…...
C++学习:类的使用--运算符重载
我们知道C可以对函数进行重载,让同名的函数来完成相同的基本操作。其实运算符也是可以重载的,而且有的运算符已经在使用了,就像*,既可以用于地址,又可以用于乘法。 下面是一个计算时间的类 #ifndef MYTIME_H #define…...

嵌入式养成计划-46----QT--简易版网络聊天室实现--QT如何连接数据库
一百一十九、简易版网络聊天室实现 119.1 QT实现连接TCP协议 119.1.1 基于TCP的通信流程 119.1.2 QT中实现服务器过程 使用QTcpServer实例化一个服务器对象设置监听状态,通过listen()函数,可以监听特定的主机,也可以监听所有客户端&#x…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...