@Transactional事务注解的批量回滚机制

关键机制说明:

1.​​事务注解生效​​:

@Transactional(rollbackFor = Exception.class)

@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveUser(UserDTO userDto) {SysUser sysUser = new SysUser();BeanUtils.copyProperties(userDto, sysUser);sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);sysUser.setPassword(ENCODER.encode(userDto.getPassword()));baseMapper.insert(sysUser);List<SysUserRole> userRoleList = userDto.getRole().stream().map(roleId -> {SysUserRole userRole = new SysUserRole();userRole.setUserId(sysUser.getUserId());userRole.setRoleId(roleId);return userRole;}).collect(Collectors.toList());return sysUserRoleService.saveBatch(userRoleList);
}
  • 该注解将整个方法纳入​​同一个数据库事务​​。
  • rollbackFor = Exception.class 指定了所有异常(包括 RuntimeException 和受检异常)都会触发回滚。
​2.事务回滚逻辑​​:
  • 事务内任何操作抛出异常(如数据库约束冲突、网络中断等),整个事务会回滚到方法开始前的状态。
  • 本方法包含两个数据库操作:
    • baseMapper.insert(sysUser):插入用户记录
    • sysUserRoleService.saveBatch(userRoleList):批量插入用户角色关联记录
  • ​这两个操作共享同一个事务​​,任何一步失败都会回滚所有操作。
 ​​3.批量保存的异常行为​​:
  • MyBatis-Plus 的 saveBatch() 在默认配置下,如果批量操作中​​任意一条数据失败​​,​​会抛出异常​​(如 DataAccessException)。
  • 此时事务管理器会捕获异常,并回滚整个事务。

需要注意的细节:

  • ​批量操作实际执行方式​​:

    • saveBatch() 默认可能通过 ​​分批提交 SQL​​(如每批 1000 条)执行。但如果某条数据失败(如唯一键冲突),MyBatis-Plus 会抛出异常,触发回滚,​​已插入的本批次数据也会回滚​​。
  • ​自调用问题​​:

    • 如果该方法被​​同一个类中的其他方法直接调用​​(而非通过 Spring 代理对象),@Transactional 会失效。确保通过 Spring 容器管理的方法调用。

验证场景:

// 模拟批量保存时部分数据失败(如角色ID不存在)
List<SysUserRole> userRoleList = Arrays.asList(
new SysUserRole(sysUser.getUserId(), 1), // 有效数据
new SysUserRole(sysUser.getUserId(), 999) // 无效数据(触发外键冲突)
);

sysUserRoleService.saveBatch(userRoleList); // 失败抛出异常

  • ​结果​​:sysUser 的插入操作也会被回滚,数据库无新增用户和角色关联记录。

总结:

只要批量保存中有一条记录失败抛出异常,整个事务回滚,确保用户数据和关联角色数据的一致性​​。这是事务 ACID 特性(原子性)的直接体现。


如果将上面的mybatisplus提供的saveBatch批量增加改成自己用for循环单条增加数据,事务还起作用吗?

答案:

​事务仍然会起作用,而且效果相同!​​ 只要循环中的任意一次单条保存失败,​​整个事务都会回滚​​(包括 baseMapper.insert(sysUser) 插入的用户记录和之前成功的角色记录)。

详细分析:

1. 修改后的代码示例(使用循环单条保存):
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveUser(UserDTO userDto) {// 插入用户记录SysUser sysUser = new SysUser();BeanUtils.copyProperties(userDto, sysUser);sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);sysUser.setPassword(ENCODER.encode(userDto.getPassword()));baseMapper.insert(sysUser);// 循环单条保存用户角色List<SysUserRole> userRoleList = ...; // 同上for (SysUserRole userRole : userRoleList) {sysUserRoleService.save(userRole); // ✨ 改为循环单条保存}return true;
}
2. 事务为何依然有效?
关键因素说明
​Spring 事务管理机制​@Transactional 会为整个方法创建一个​​数据库连接级的事务上下文​​。
​同一事务上下文​循环中的每次 sysUserRoleService.save() 操作与用户插入操作共享同一个事务。
​异常传播​循环中任意一次保存失败抛出异常时,异常会传播到 @Transactional 注解层,触发全局回滚。

3. 执行流程(含错误场景):
  1. ​正确流程​​:
    用户插入 → 角色1保存 → 角色2保存 → ... → 全部成功 → ​​事务提交​

  2. ​错误流程​​(假设第3次保存失败):
    用户插入 → 角色1保存 → 角色2保存 → ❌ 角色3保存失败 → 抛出异常 → ​​事务回滚​
    → ​​已插入的用户和角色1、2记录均被撤销​


重要注意事项:

1. ​​性能陷阱​

⚠️ 将 saveBatch() 批量操作改为循环单条保存会​​严重降低性能​​:

  • ​N+1 问题​​:每条数据单独执行一次 SQL(产生 N 次网络IO + SQL 解析开销)
  • ​对比​​:saveBatch() 默认会合并为单条 SQL 或小批量提交(如 INSERT INTO table VALUES (...), (...), ...
2. ​​异常处理建议​

避免在循环内捕获异常后继续执行(除非明确需要部分提交):

// ❌ 错误做法(导致事务失效):
for (SysUserRole userRole : userRoleList) {
try {
sysUserRoleService.save(userRole);
} catch (Exception e) {
// 捕获后不抛出,事务无法感知异常,继续提交后续数据!
}
}

3. ​​嵌套事务风险​

如果 sysUserRoleService.save() 也有 @Transactional

  • 默认传播行为 (REQUIRED) 会加入当前事务 → ​​安全,行为一致​
  • 若改为 REQUIRES_NEW 则每次循环新建独立事务 → ​​破坏原子性(部分提交)​

结论:

  1. ​事务有效​​:循环单条保存不会破坏事务的原子性,失败时仍会全局回滚。
  2. ​避免滥用循环​​:务必优先使用批量操作(如 saveBatch())以保证性能。
  3. ​统一事务上下文​​:只要不修改默认的传播行为,嵌套调用的操作仍在同一事务中。

 

 

 

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

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

相关文章

飞算 JavaAI 深度体验:开启 Java 开发智能化新纪元

个人主页&#xff1a;♡喜欢做梦 欢迎 &#x1f44d;点赞 ➕关注 ❤️收藏 &#x1f4ac;评论 目录 一、引言 二、飞算 JavaAI 初印象与功能概览 &#xff08;一&#xff09;初识飞算 JavaAI &#xff08;二&#xff09;核心功能模块概览 三、智能代码生成功能深度体…

pandas销售数据分析

pandas销售数据分析 数据保存在data目录 消费者数据&#xff1a;customers.csv商品数据&#xff1a;products.csv交易数据&#xff1a;transactions.csv customers.csv数据结构&#xff1a;字段描述customer_id客户IDgender性别age年龄region地区membership_date会员日期produc…

访问Windows服务器备份SQL SERVER数据库

以前没有直接访问过Windows服务器,今天刚一看到的是时候有点懵,竟然下意识的使用SecureCRT远程工具去连了一下,然后领导说,看一下用户名,突然意识到,跟我们平时远程桌面是一样的。 一、 win + R 打开命令窗口 二、 输入 mstsc 三、 输入远程地址 四、点击连接,如果有弹…

C++ 面向对象 - 对象定义方法汇总

C对象定义方法汇总 1. 栈上定义方式 1.1 调用无参构造函数的定义方式 无参构造函数有两种&#xff1a; 默认无参构造函数Demo(){}默认值列表构造函数。Demo():a{1},b{2}{} // 使用初始化列表实现对象定义方式&#xff1a; Demo d; Demo d1{}; // 以下定义方式还调用了拷贝构造…

指尖上的魔法:优雅高效的Linux命令手册

一、Linux基础指令 1. ls ls&#xff1a;对于目录&#xff0c;列出该目录下的所有子目录与文件&#xff0c;对于文件&#xff0c;将列出文件名以及其他信息。 -a&#xff1a;列出目录下的所有文件&#xff0c;包含以.开头的隐藏文件 -l:列出文件的详细信息 -d&#xff1a;将目录…

《磁力下载工具实测:资源搜索+高速下载一站式解决方案》

嘿&#xff0c;朋友们&#xff01;我是阿灿&#xff0c;今天给大家带来一个超实用的看片神器&#xff0c;特别适合老司机们使用&#xff0c;保证让你眼前一亮&#xff01;推荐一款比某雷更好用的下载工具&#xff0c;搭配资源搜索神器&#xff0c;轻松获取资源不限速。超强磁力…

Go网络编程基础:网络模型与协议栈概述 - 从理论到实践的完整指南

1. 引言 在当今的互联网时代&#xff0c;网络编程已经成为后端开发的核心技能。Go语言以其出色的并发性能和简洁的语法&#xff0c;在网络编程领域展现出了强大的优势。从Docker、Kubernetes到众多微服务框架&#xff0c;Go已经成为构建高性能网络应用的首选语言之一。 你是否…

Web攻防-SSTI服务端模版注入利用分类语言引擎数据渲染项目工具挖掘思路

知识点&#xff1a; 1、WEB攻防-SSTI-利用分类&功能点 2、WEB攻防-SSTI-利用项目&挖掘思路 SSTI(Server Side Template Injection) 服务器模板注入, 服务端接收了用户的输入&#xff0c;将其作为 Web 应用模板内容的一部分&#xff0c;在进行目标编译渲染的过程中&…

李沐动手学深度学习Pytorch-v2笔记【07自动求导代码实现】

文章目录前言自动求导实现非标量变量的反向传播分离计算Python控制流的梯度计算前言 关于走动求导的理论知识个人有点难以理解&#xff0c;推荐大家去看https://blog.csdn.net/weixin_42831564/article/details/135658138这篇文章&#xff0c;讲的很好。 自动求导实现 impor…

strchr 与 strstr 函数详解

一.strchr - 字符查找函数1.函数原型char *strchr(const char *str, int c);2.核心功能在字符串中查找特定字符的第一次出现位置3.参数说明参数 类型 说明str const char* 要搜索的字符串c int 要查找的字符&#xff08;自动转换为char&#xff09;4.返回值…

jakes信道模型

Jakes 模型 前面我们介绍了多径信道合成信号可表示为&#xff1a; r(t)Re{∑i0N(t)−1ai(t)u(t−τi(t))ej2πfc(t−τi(t))ϕDi(t)} r(t)Re \left\{\sum_{i0}^{N(t)-1}a_{i}(t)u(t-\tau_{i}(t))e^{j2\pi f_{c}(t-\tau_{i}(t))\phi_{D_{i}}(t)} \right\} r(t)…

JVM类加载机制解析

什么是类加载器&#xff1f; 类加载器是JVM的核心组件之一&#xff0c;负责将Java字节码文件&#xff08;.class文件&#xff09;加载到JVM内存中。由于JVM只能执行二进制字节码&#xff0c;类加载器的作用就是将编译后的.class文件转换为JVM可以理解和执行的格式&#xff0c;使…

用Python和OpenCV从零搭建一个完整的双目视觉系统(二)

本系列文章旨在系统性地阐述如何利用 Python 与 OpenCV 库&#xff0c;从零开始构建一个完整的双目立体视觉系统。 本项目github地址&#xff1a;https://github.com/present-cjn/stereo-vision-python.git 项目架构设计&#xff1a;蓝图、分工与工作流 在上一篇文章中&#…

亿级流量下的缓存架构设计:Redis+Caffeine多级缓存实战

亿级流量下的缓存架构设计&#xff1a;RedisCaffeine多级缓存实战 一、为什么需要多级缓存&#xff1f; 在亿级流量场景下&#xff0c;单纯依赖Redis会遇到三大瓶颈&#xff1a;网络延迟&#xff1a;Redis远程访问通常需要1-5ms&#xff0c;QPS超过10万时成为瓶颈资源成本&…

AI基建还能投多久?高盛:2-3年不是问题,回报窗口才刚开启

高盛表示&#xff0c;尽管AI商业化变现仍处早期阶段&#xff0c;但基于成本削减的第一阶段回报已经显现。预测到2030年AI自动化可为财富500强企业节省约9350亿美元成本。分析师认为&#xff0c;这一早期收益足以支撑当前AI基础设施投资水平&#xff0c;尽管增长率可能放缓。虽然…

【mac】快捷键使用指南

在Mac上&#xff0c;根据选择对象的不同&#xff0c;在选择时移动的方法也有所不同&#xff0c;以下是具体介绍&#xff1a; 移动文件或文件夹&#xff1a;可通过拖放操作移动。打开“访达”&#xff08;Finder&#xff09;&#xff0c;找到要移动的文件或文件夹&#xff0c;按…

CS144 lab2 tcp_receiver

1. 实验目的 lab2 的目的是实现tcp的接收端。 主要包括两方面 &#xff08;1&#xff09; 从发送端接收消息&#xff0c;使用Reassembler聚合字节流&#xff08;Bytestream&#xff09; &#xff08;2&#xff09;将确认号&#xff08;ackno&#xff09;和window size发回对端 …

【论文笔记】A Deep Reinforcement Learning Based Real-Time Solution Policy for the TSP

《基于 DRL 和 DCNN 的实时 TSP 求解策略》IEEE TRANSACTIONS ON INTELLIGENT TRANSPORTATION SYSTEMS, VOL. 24, NO. 6, JUNE 2023一段话总结本文提出了一种基于深度强化学习&#xff08;DRL&#xff09; 和深度卷积神经网络&#xff08;DCNN&#xff09; 的实时旅行商问题&am…

MMaDA:多模态大型扩散语言模型

集众家之所长&#xff0c;成大一统。普林斯顿大学、北京大学、清华大学、字节跳动的研究者将“文本推理、多模态分析、图像生成”三大方向融合在一个单一扩散模型里&#xff0c;并用恰当的优化策略来提升模型在各个方向的性能。 研究动机 研究人员致力于开发一个能够处理多种模…

容器技术入门与Docker环境部署

容器技术入门与Docker环境部署Docker概述什么是 DockerDocker 的优势Docker 的应用场景Docker 核心概念(1)镜像(2)容器(3)仓库Docker 安装1.关闭系统防火墙和内核2.下载Docker的repo文件3.替换仓库地址4.更新索引文件并安装Docker5.添加国内镜像站6.开启Docker服务7.优化内核参…