10 种 Spring事务失效场景
10 种 Spring事务失效场景
1.概述
Spring针对Java Transaction API (JTA)、JDBC、Hibernate和Java Persistence API(JPA)等事务 API,实现了一致的编程模型,而Spring的声明式事务功能更是提供了极其方便的事务配置方式,配合Spring Boot的自动配置,大多数Spring Boot项目只需要在方法上标记@Transactional注解,即可一键开启方法的事务性配置。
但是,事务如果没有被正确出,很有可能会导致事务的失效,带来意想不到的数据不一致问题,随后就是大量的人工接入查看和修复数据,该篇主要分享Spring事务在技术上的正确使用方式,避免因为事务处理不当导致业务逻辑产生大量偶发性BUG。
在分析事务失效的常见场景之前,我们先来了解一下:事务的传播类型 和 @Transactionnal 注解的不同属性的含义。
2.事务的传播类型
//如果有事务, 那么加入事务, 没有的话新建一个(默认)
@Transactional(propagation=Propagation.REQUIRED)
//容器不为这个方法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
//不管是否存在事务, 都创建一个新的事务, 原来的挂起, 新的执行完毕, 继续执行老的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
//必须在一个已有的事务中执行, 否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)
//必须在一个没有的事务中执行, 否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
//如果其他bean调用这个方法, 在其他bean中声明事务, 那就用事务, 如果其他bean没有声明事务, 那就不用事务
@Transactional(propagation=Propagation.SUPPORTS)
3.isolation
该属性用于设置底层数据库的事务隔离级别,事务的隔离级别介绍:
// 读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// 读取已提交数据(会出现不可重复读和幻读) Oracle默认
@Transactional(isolation = Isolation.READ_COMMITTED)
// 可重复读(会出现幻读) MySQL默认
@Transactional(isolation = Isolation.REPEATABLE_READ)
// 串行化
@Transactional(isolation = Isolation.SERIALIZABLE)
4.@Transactionnal注解属性
@Transactional注解可以作用于接口、接口方法、类以及类方法上,它可以通过不同的参数来选择什么类型Exception异常下执行回滚或者不回滚操作。

5.Spring事务失效的场景
1. 事务方法未被Spring管理
如果事务方法所在的类没有注册到Spring IOC容器中,也就是说,事务方法所在类并没有被Spring管理,则Spring事务会失效,举个例子🌰:
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {@Autowiredprivate ProductMapper productMapper;@Override@Transactional(propagation = Propagation.REQUIRES_NEW)public void updateProductStockById(Integer stockCount, Long productId) {productMapper.updateProductStockById(stockCount, productId);}
}
ProductServiceImpl实现类上没有添加@Service注解,Product的实例也就没有被加载到Spring IOC容器,此时updateProductStockById()方法的事务就会在Spring中失效。
2. 方法使用final类型修饰
有时候,某个方法不想被子类重写,这时可以将该方法定义成final的。普通方法这样定义是没问题的,但如果将事务方法定义成final,例如:
@Service
public class OrderServiceImpl {@Transactionalpublic final void cancel(OrderDTO orderDTO) {// 取消订单cancelOrder(orderDTO);}
}
OrderServiceImpl的cancel取消订单方法被final修饰符修饰,Spring事务底层使用了AOP,也就是通过JDK动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,从而无法添加事务功能。这种情况事务就会在Spring中失效。
3. 非public修饰的方法
如果事务方式不是public修饰,此时Spring事务会失效,举个例子🌰:
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {@Autowiredprivate ProductMapper productMapper;@Override@Transactional(propagation = Propagation.REQUIRES_NEW)private void updateProductStockById(Integer stockCount, String productId) {productMapper.updateProductStockById(stockCount, productId);}
}
虽然ProductServiceImpl添加了@Service注解,同时updateProductStockById()方法上添加了@Transactional(propagation = Propagation.REQUIRES_NEW)注解,但是由于事务方法updateProductStockById()被 private 定义为方法内私有,同样Spring事务会失效。
4. 同一个类中的方法相互调用
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate ProductMapper productMapper;@Overridepublic ResponseEntity submitOrder(Order order) {// 保存生成订单信息long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));order.setOrderNo("ORDER_" + orderNo);orderMapper.insert(order);// 扣减库存this.updateProductStockById(order.getProductId(), 1L);return new ResponseEntity(HttpStatus.OK);}@Transactional(propagation = Propagation.REQUIRES_NEW)public void updateProductStockById(Integer num, Long productId) {productMapper.updateProductStockById(num, productId);}
}
submitOrder()方法和updateProductStockById()方法都在OrderService类中,然而submitOrder()方法没有添加事务注解,updateProductStockById()方法虽然添加了事务注解,这种情况updateProductStockById()会在Spring事务中失效。
5. 方法的事务传播类型不支持事务
如果内部方法的事务传播类型为不支持事务的传播类型,则内部方法的事务同样会在Spring中失效,举个例子:
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate ProductMapper productMapper;@Override@Transactional(propagation = Propagation.REQUIRES_NEW)public ResponseEntity submitOrder(Order order) {long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));order.setOrderNo("ORDER_" + orderNo);orderMapper.insert(order);// 扣减库存this.updateProductStockById(order.getProductId(), 1L);return new ResponseEntity(HttpStatus.OK);}/*** 扣减库存方法事务类型声明为NOT_SUPPORTED不支持事务的传播*/@Transactional(propagation = Propagation.NOT_SUPPORTED)public void updateProductStockById(Integer num, Long productId) {productMapper.updateProductStockById(num, productId);}
}
6.异常被内部catch,程序生吞异常
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate ProductMapper productMapper;@Override@Transactionalpublic ResponseEntity submitOrder(Order order) {long orderNo = Math.abs(ThreadLocalRandom.current().nextLong(1000));order.setOrderNo("ORDER_" + orderNo);orderMapper.insert(order);// 扣减库存this.updateProductStockById(order.getProductId(), 1L);return new ResponseEntity(HttpStatus.OK);}/*** 扣减库存方法事务类型声明为NOT_SUPPORTED不支持事务的传播*/@Transactional(propagation = Propagation.REQUIRES_NEW)public void updateProductStockById(Integer num, Long productId) {try {productMapper.updateProductStockById(num, productId);} catch (Exception e) {// 这里仅仅是捕获异常之后的打印(相当于程序吞掉了异常)log.error("Error updating product Stock: {}", e);}}
}
7. 数据库不支持事务
Spring事务生效的前提是连接的数据库支持事务,如果底层的数据库都不支持事务,则Spring事务肯定会失效的,例如🌰:使用MySQL数据库,选用MyISAM存储引擎,因为MyISAM存储引擎本身不支持事务,因此事务毫无疑问会失效。
8. 未配置开启事务
如果项目中没有配置Spring的事务管理器,即使使用了Spring的事务管理功能,Spring的事务也不会生效,例如,如果你是Spring Boot项目,没有在SpringBoot项目中配置如下代码:
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);
}
如果是以往的Spring MVC项目,如果没有配置下面的代码,Spring事务也不会生效,正常需要在applicationContext.xml文件中,手动配置事务相关参数,比如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!--配置要扫描的包--><context:component-scan base-package="com.llp.spring.tx.dao"/><context:component-scan base-package="com.llp.spring.tx.service"/><!--引入外部的jdbc.properties文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--配置数据源对象-DataSoruce--><bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"><!--给数据源对象配置属性值--><property name="user" value="${jdbc.user}"/><property name="password" value="${jdbc.pwd}"/><property name="driverClass" value="${jdbc.driver}"/><property name="jdbcUrl" value="${jdbc.url}"/></bean><!--配置JdbcTemplate对象--><bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate"><!--给JdbcTemplate对象配置dataSource--><property name="dataSource" ref="dataSource"/></bean><!--配置事务管理器-对象1. DataSourceTransactionManager 这个对象是进行事务管理-debug源码2. 一定要配置数据源属性,这样指定该事务管理器 是对哪个数据源进行事务控制--><bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"><property name="dataSource" ref="dataSource"/></bean><!--配置启动基于注解的声明式事务管理功能--><tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
9. 错误的传播特性
其实,我们在使用@Transactional注解时,是可以指定propagation参数的。
该参数的作用是指定事务的传播特性,目前Spring支持7种传播特性:
REQUIRED如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。SUPPORTS如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。MANDATORY如果当前上下文中存在事务,否则抛出异常。REQUIRES_NEW每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。NOT_SUPPORTED如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。NEVER如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。NESTED如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
如果我们在手动设置propagation参数的时候,把传播特性设置错了,比如:
@Service
public class OrderServiceImpl {@Transactional(propagation = Propagation.NEVER)public void cancelOrder(UserModel userModel) {// 取消订单cancelOrder(orderDTO);// 还原库存restoreProductStock(orderDTO.getProductId(), orderDTO.getProductCount());}
}
我们可以看到cancelOrder()方法的事务传播特性定义成了Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则会抛异常。
10. 多线程调用
在实际项目开发中,多线程的使用场景还是挺多的。如果Spring事务用在多线程场景中使用不当,也会导致事务无法生效。
@Slf4j
@Service
public class OrderServiceImpl {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate MessageService messageService;@Transactionalpublic void orderCommit(orderModel orderModel) throws Exception {orderMapper.insertOrder(orderModel);new Thread(() -> {messageService.sendSms();}).start();}
}@Service
public class MessageService {@Transactionalpublic void sendSms() {// 发送短信}
}
通过示例,我们可以看到订单提交的事务方法orderCommit()中,调用了发送短信的事务方法sendSms(),但是发送短信的事务方法sendSms()是另起了一个线程调用的。
这样会导致两个方法不在同一个线程中,从而是两个不同的事务。如果是sendSms()方法中抛了异常,orderCommit()方法也回滚是不可能的。
实际上,Spring的事务是通过ThreadLocal来保证线程安全的,事务和当前线程绑定,多个线程自然会让事务失效。

相关文章:
10 种 Spring事务失效场景
10 种 Spring事务失效场景 1.概述 Spring针对Java Transaction API (JTA)、JDBC、Hibernate和Java Persistence API(JPA)等事务 API,实现了一致的编程模型,而Spring的声明式事务功能更是提供了极其方便的事务配置方式,配合Spring Boot的自动…...
重读《DOOM启世录》
许多游戏开发者都是网瘾少年,抱着对游戏的热爱进入游戏行业,在经历996的加班加点,买房的压力,浮躁同样跟随着我们,我们是否还热爱着自己的事业,我们不是天才,也成不了卡马克,但是我们…...
巧用性格上的差异来组建团队
你好,我是得物 App 交易平台及中间件平台的 Team Leader Alan。 组建团队过程中,你有没有遇到过类似的场景:团队中某些人之间总是互相不对付、气场不合,不管是日常沟通中还是方案对齐,总是会出现面红耳赤的场面。 从…...
Leetcode11. 盛最多水的容器
一、题目描述: 给定一个长度为 nnn 的整数数组 heightheightheight 。有 nnn 条垂线,第 iii 条线的两个端点是 (i,0)(i, 0)(i,0) 和 (i,height[i])(i, height[i])(i,height[i]) 。 找出其中的两条线,使得它们与 xxx 轴共同构成的容器可以容…...
Java笔记026-集合/数组、Collection接口、ArrayList、Vector、LinkedList
集合集合的理解和好处保存多个数据使用的是数组,分析数组的弊端数组1、长度开始必须指定,而且一旦指定,不能更改2、保存的必须为同一类型的元素3、使用数组进行增加/删除元素的示意代码-比较麻烦Person数组扩容示意代码Person[] pers new Pe…...
Hive学习——分桶抽样、侧视图与炸裂函数搭配、hive实现WordCount
目录 一、分桶抽样 1.抽取表中10%的数据 2.抽取表中30%的数据 3.取第一行 4.取第10行 5.数据块抽样 6.tablesample详解 二、UDTF——表生成函数 1.explode()——炸裂函数 2.posexpolde()——只能对array进行炸裂 3.inline()——炸裂结构体数组 三、UDTF与侧视图的搭…...
大数据算法
1. TOP K 算法 有10个⽂件,每个⽂件1G,每个⽂件的每⼀⾏存放的都是⽤户的 query,每个⽂件的 query 都可能重复。要求你按照 query 的频度排序。 方法1: 顺序读取10个⽂件,按照 hash(query)%10 的结果将 query 写⼊到…...
非暴力沟通读书笔记
浅读《非暴力沟通》,本书对于沟通的方式总结成了一个方法论,从13个章节去概述非暴力沟通的方法和重点。其中最重要的是非暴力沟通四要素,观察、感受、需要、请求。同时在沟通中注意观察,投入爱,重视倾听的力量…...
代码随想录【Day21】| 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先
530. 二叉搜索树的最小绝对差 题目链接 题目描述: 给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。 示例: 提示:树中至少有 2 个节点。 难点: 解答错误!仅考虑了…...
注意啦,面试通过后,别忘了教师资格证认定
所有要「教师资格证认定」教程的宝子们看过来面试合格的小伙伴都可以进行认定工作 . 认定时间 查询各省份认定公告,确定认定时间范围。以下是公告汇总网址(https://www.jszg.edu.cn/portal/qualification_cert/dynamics?id21691) 认定次数 每…...
【LeetCode】No.154. 寻找旋转排序数组中的最小值 II -- Java Version
题目链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array-ii/ 1. 题目介绍(154. 寻找旋转排序数组中的最小值 II) 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后࿰…...
RestTemplate远程调用
我们现在项目中使用的RPC远程调用技术是Dubbo实际上除了Dubbo技术之外,还有很多远程调用的方法它们有些调用的思想都和Dubbo完全不同Dubbo是SpringCloudAlibaba提供的功能强大的RPC框架但是Dubbo功能也有限制,如果我们想调用的方法不是我们当前项目的组件或功能,甚至想调用的方…...
registerForActivityResult使用
目录 针对 activity 结果注册回调 启动 activity 以获取其结果 在单独的类中接收 activity 结果 测试 创建自定义协定 registerForActivityResult()是startActivityForResult()的替代,简化了数据回调的写法 启动另一个 activity&#x…...
工作中,python真的有用吗?
普通上班族学Python有用吗? 那么,我也在这里提出一个问题:Python究竟适不适合办公人士来学习,以及学了之后究竟能不能给我的工作来带质一般的飞跃? 以我的亲身经历为例,我可以很负责的告诉大家,…...
固态继电器控制电路
固态继电器控制电路 固态继电器(SSR)的种类和型号很多,因此其输入控制方法和控制电路也相应众多。固态继电器(SSR)的共同特点在于驱动电流或驱动电压小,即只需输入一个小信号即可控制SSR的开关。 如果需要…...
数仓、数据湖、湖仓一体、数据网格的探索与研究
第一代:数据仓库 定义 为解决数据库面对数据分析的不足,孕育出新一类产品数据仓库。数据仓库(Data Warehouse)是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策和信息的全局共享。 数…...
设计模式系列 - 备忘录模式
介绍&定义 备忘录模式,也叫快照(Snapshot)模式,英文翻译是 Memento Design Pattern。在 GoF 的《设计模式》一书中,备忘录模式是这么定义的: Captures and externalizes an object’s internal state…...
详细介绍React生命周期和diffing算法
事件处理 1.通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —— 为了更好的兼容性;React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ——为了的高效。 2.通过event.target得到发生事件的DOM…...
面向对象的特点
1、什么是对象对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物。在面向对象程序设计中,对象所指的是计算机系统中的某一个成分。在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个…...
智慧校园平台源码 智慧教务 智慧电子班牌系统
系统介绍 智慧校园系统是通过信息化手段,实现对校园内各类资源的有效集成 整合和优化,实现资源的有效配置和充分利用,将校务管理过程的优化协调。为校园提供数字化教学、数字化学习、数字化科研和数字化管理。 致力于为家长和教师提供一个全方位、多层…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
