Springboot事务控制中A方法调用B方法@Transactional生效与不生效情况实战总结
介绍
本篇对Springboot事务控制中A方法调用B方法@Transactional生效与不生效情况进行实战总结,让容易忘记或者困扰初学者甚至老鸟的开发者,只需要看这一篇文章即可立马找到解决方案,这就是干货的价值。喜欢的朋友别忘记来个一键三连哈:)
实战步骤
由于A方法调用B方法的情况较多,此处按照 一定的命名规则复现各种情况。
例如:
c代表class,c0代表不同类,c1代表同类
a代表a方法,a0代表a方法无事务注解,a1代表有
b代表b方法,b0代表b方法无事务注解,b1代表有
e代表抛异常,ea代表a方法中抛异常;eb代表b方法执行抛异常;
组合起来:c1a0b1ea 表示:同类中a调用b,b上有事务,a中抛异常
创建表
CREATE TABLE `tb_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 36 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
创建测试类
AbstractUserService执行事务操作
@Service
public class AbstractUserService {@Autowiredprivate UserService userService;/*** 新增用户* @param username*/public void saveUser(String username) {UserEntity userEntity = new UserEntity();userEntity.setUsername(username);userService.save(userEntity);}/*** 更新密码* @param username*/public void updatePassword(String username) {UserEntity entity = userService.getOne(new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getUsername,username));entity.setPassword("123456");userService.updateById(entity);}/*** 制造异常*/public void makeException() {int i=1/0;}
}
Service01 代表a类
@Service
public class Service01 extends AbstractUserService{@Autowiredprivate Service02 service02;// 同类调用public void c1_a0_b1_ea(String username){saveUser(username);this.b1(username,false);makeException();}public void c1_a0_b1_eb(String username){saveUser(username);this.b1(username,true);}@Transactional(rollbackFor = Exception.class)public void c1_a1_b0_ea(String username){saveUser(username);this.b0(username,false);makeException();}@Transactional(rollbackFor = Exception.class)public void c1_a1_b0_eb(String username){saveUser(username);this.b0(username,true);}@Transactional(rollbackFor = Exception.class)public void c1_a1_b1_ea(String username){saveUser(username);this.b1(username,false);makeException();}@Transactional(rollbackFor = Exception.class)public void c1_a1_b1_eb(String username){try{saveUser(username);this.b1(username,true);}catch (Exception e){System.out.println("c1_a1_b1_eb执行失败:");throw new RuntimeException("c1_a1_b1_eb执行失败");}}// 不同类调用public void c0_a0_b1_ea(String username){saveUser(username);service02.b1(username,false);makeException();}public void c0_a0_b1_eb(String username){saveUser(username);service02.b1(username,true);}@Transactional(rollbackFor = Exception.class)public void c0_a1_b0_ea(String username){saveUser(username);service02.b0(username,false);makeException();}@Transactional(rollbackFor = Exception.class)public void c0_a1_b0_eb(String username){saveUser(username);service02.b0(username,true);}@Transactional(rollbackFor = Exception.class)public void c0_a1_b1_ea(String username){saveUser(username);service02.b1(username,false);makeException();}@Transactional(rollbackFor = Exception.class)public void c0_a1_b1_eb(String username){saveUser(username);service02.b1(username,true);}public void b0(String username,boolean hasException){updatePassword(username);if(hasException){makeException();}}@Transactional(rollbackFor = Exception.class)public void b1(String username, boolean hasException){updatePassword(username);if(hasException){makeException();}}}
Service02 代表b类
@Service
public class Service02 extends AbstractUserService{public void b0(String username,boolean hasException){updatePassword(username);if(hasException){makeException();}}@Transactional(rollbackFor = Exception.class)public void b1(String username, boolean hasException){updatePassword(username);if(hasException){makeException();}}
}
测试接口TestController
@RestController
@RequestMapping("")
public class TestController {@Autowiredprivate Service01 service01;/*** 同类* a没有事务,b有 ,异常发生在a中 不会回滚* @return*/@GetMapping("/c1_a0_b1_ea")public String test1() {service01.c1_a0_b1_ea("c1_a0_b1_ea");return "ok";}/*** 同类* a没有事务,b有 ,异常发生在b中 不会回滚* @return*/@GetMapping("/c1_a0_b1_eb")public String test2() {service01.c1_a0_b1_eb("c1_a0_b1_eb");return "ok";}/*** 同类* a有事务,b没有 ,异常发生在a中 会回滚* @return*/@GetMapping("/c1_a1_b0_ea")public String test3() {service01.c1_a1_b0_ea("c1_a1_b0_ea");return "ok";}/*** 同类* a有事务,b没有 ,异常发生在b中 会回滚* @return*/@GetMapping("/c1_a1_b0_eb")public String test4() {service01.c1_a1_b0_eb("c1_a1_b0_eb");return "ok";}/*** 同类* a有事务,b有 ,异常发生在a中 会回滚* @return*/@GetMapping("/c1_a1_b1_ea")public String test5() {service01.c1_a1_b1_ea("c1_a1_b1_ea");return "ok";}/*** 同类* a有事务,b有 ,异常发生在b中 会回滚* @return*/@GetMapping("/c1_a1_b1_eb")public String test6() {service01.c1_a1_b1_eb("c1_a1_b1_eb");return "ok";}/*** 不同类* a没有事务,b有 ,异常发生在a中 不会回滚* @return*/@GetMapping("/c0_a0_b1_ea")public String test7() {service01.c0_a0_b1_ea("c0_a0_b1_ea");return "ok";}/*** 不同类* a没有事务,b有 ,异常发生在b中 只有b回滚* @return*/@GetMapping("/c0_a0_b1_eb")public String test8() {service01.c0_a0_b1_eb("c0_a0_b1_eb");return "ok";}/*** 不同类* a有事务,b没有 ,异常发生在a中 会回滚* @return*/@GetMapping("/c0_a1_b0_ea")public String test9() {service01.c0_a1_b0_ea("c0_a1_b0_ea");return "ok";}/*** 不同类* a有事务,b没有 ,异常发生在b中 会回滚* @return*/@GetMapping("/c0_a1_b0_eb")public String test10() {service01.c0_a1_b0_eb("c0_a1_b0_eb");return "ok";}/*** 不同类* a有事务,b有 ,异常发生在a中 会回滚* @return*/@GetMapping("/c0_a1_b1_ea")public String test11() {service01.c0_a1_b1_ea("c0_a1_b1_ea");return "ok";}/*** 不同类* a有事务,b有 ,异常发生在b中 会回滚* @return*/@GetMapping("/c0_a1_b1_eb")public String test12() {service01.c0_a1_b1_eb("c0_a1_b1_eb");return "ok";}
}
测试结果
http://localhost:9000/test/c0_a1_b1_eb
在浏览器中依次访问测试接口中的每个方法,得到表中结果:
以下情况未回滚或者半回滚,不在表里的均正常回滚事务。

原理总结
原理:
spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。
此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction,我们看到的现象就是@Transactional注解无效。
那回到一开始的问题,我们调用的方法A不带注解,因此代理类不开事务,而是直接调用目标对象的方法。当进入目标对象的方法后,执行的上下文已经变成目标对象本身了,因为目标对象的代码是我们自己写的,和事务没有半毛钱关系,此时你再调用带注解的方法,照样没有事务,只是一个普通的方法调用而已。
简单来说,内部调用本类方法,不会再走代理了,所以B的事务不起作用。
如果AB不同类,A调用的事代理类B,故B有事务。
参考文章
- https://blog.csdn.net/weixin_36586564/article/details/105687331
- https://juejin.cn/post/7031446300142862373
- 【@Transactional注解失效的几种情况】
https://blog.csdn.net/Yaml4/article/details/138123693
相关文章:
Springboot事务控制中A方法调用B方法@Transactional生效与不生效情况实战总结
介绍 本篇对Springboot事务控制中A方法调用B方法Transactional生效与不生效情况进行实战总结,让容易忘记或者困扰初学者甚至老鸟的开发者,只需要看这一篇文章即可立马找到解决方案,这就是干货的价值。喜欢的朋友别忘记来个一键三连哈&#x…...
python -【三】循环语句
一、while 循环 while 语法 while 条件: 条件满足时,做事情 a 0 while a < 100:print(i like python ...)a 1求 1-100 的总和 i 1 sum 0 while i < 100:sum ii 1 print(f1-100 的和是 {sum})""" 1-100 的和是 5050 ""&…...
类的内存对齐位段位图布隆过滤器哈希切割一致性哈希
文章目录 一、类的内存对齐1.1规则1.2原因 二、位段2.1介绍2.2内存分配问题2.3跨平台问题2.4使用的注意事项 三、位图的应用3.1 给40亿个不重复的无符号整数,找给定的一个数。(int的范围可以到达42亿多)3.2 给定100亿个整数,设计算…...
于ThinkPHP开发的赛事报名小程序
基于ThinkPHP开发的赛事报名微信小程序 功能包括 1、参赛公告 2、会员中心(会员注册、登录、成绩查询、资料管理、参赛记录管理) 3、个人报名和企业报名 (身份证验证防止重复报名) 4、培训报名 5、查询是否在库人员,根…...
前端学习--React部分
文章目录 前端学习--React部分前言1.React简介1.1React的特点1.2引入文件1.3JSX🍉JSX简介与使用🍉JSX语法规则 1.4模块与组件🍉模块🍉组件 1.5安装开发者工具 2.React面向组件编程2.1创建组件🍉函数式组件🍉…...
24V_2A_1.2MHZ|PCD0303升压恒频LCD背光源专用电路超小体积封装
概述 PCD0303是一个恒定频率,6针SOT23电流模式升压转换器用于小型低功耗应用。PCD0303 以1.2MHz切换,并且允许使用微小的,低成本电容器和电感器2mm或更小,内部软启动会产生较小的涌入电流延长电池寿命。PCD0303具有自动切换至轻负载下的脉冲…...
python生成词云图
生成词云图的话需要先对数据进行分词处理 , 分词方法点击查看 import pandas as pd from collections import Counter from wordcloud import WordCloud import matplotlib.pyplot as plt# 假设您已经按照之前的步骤处理了数据,并且处理后的数据保存在comments_proc…...
【使用ChatGPT构建应用程序】应用程序开发概述:1. 管理秘钥、2. 数据安全、3. 与应用程序解耦、4. 注意提示语的注入攻击
文章目录 一. 首先注意的两个方面1. 管理API密钥1.1. 用户提供API密钥1.2. 你自己提供API密钥 2. 数据安全和数据隐私 二. 软件架构设计原则:与应用程序解耦三. 注意LLM提示语的注入攻击1. 分析输入和输出2. 监控和审计3. 其他要注意的注入情况 在了解了ChatGPT的文…...
【JavaScript脚本宇宙】不可或缺的Web开发工具:图表和可视化
图形化你的数据:六款顶级JavaScript库全接触 前言 在本文中,我们将深入探讨六个强大的JavaScript库,这些库被广泛应用于数据可视化和交互式图形展示。我们将了解每个库的概述、主要特性、使用示例以及使用场景,以帮助读者更全面…...
自然语言处理(NLP)中的迁移学习
Transfer Learning in NLP 迁移学习(Transfer Learning)无疑是目前深度学习中的新热点(相对而言)。在计算机视觉领域,它已经应用了一段时间,人们使用经过训练的模型从庞大的ImageNet数据集中学习特征&…...
PLC集成BL121PO网关优化智能电网的远程管理PLC转OPC UA协议
随着工业自动化技术的不断发展,智能电网等复杂系统对于设备之间高效通信的需求日益增加。PLC转OPC UA协议转换网关BL121PO作为一款领先的协议转换设备,通过其独特的设计和功能,为用户提供了高效、安全的PLC接入OPC UA的解决方案。 设备概述 …...
爬虫案例(读书网)
一.我们还是使用简单的bs4库和lxml,使用xpath: 导入下面的库: import requests from bs4 import BeautifulSoup from lxml import etree 我们可以看见它的div和每个书的div框架,这样会观察会快速提高我们的简单爬取能力。 二.实…...
Linux系统编程(五)多线程创建与退出
目录 一、基本知识点二、线程的编译三、 线程相关函数1. 线程的创建(1)整型的传入与接收(2)浮点数的传入与接收(3)字符串的传入与接收(4)结构体的传入与接收 2. 线程的退出3. 线程的…...
计算机毕业设计 | SpringBoot个人博客管理系统(附源码)
1,绪论 1.1 背景调研 在互联网飞速发展的今天,互联网已经成为人们快速获取、发布和传递信息的重要渠道,它在人们政治、经济、生活等各个方面发挥着重要的作用。互联网上发布信息主要是通过网站来实现的,获取信息也是要在互联网中…...
字母的大小写转换
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中,字符串对象提供了lower()方法和upper()方法进行字母的大小写转换,即可用于将大写字母转换为小写字母或者将小写字…...
JTW结构
JTW(JSON Web Token)的结构 在这篇笔记中,我们将了解JTW(JSON Web Token)的结构。我们将看到JTW是如何创建的,令牌的各个部分是什么,以及您如何自己构建和构造JTW。您还将了解一些这种结构的含义,以及使用JTW进行授权时的一些结果优缺点。 基本上,JTW本质上就是一个…...
debian11安装留档@VirtualBox
因为debian12无法安装tpot,所以又把11重新安装一遍,以前的安装文档:安装Debian 11 留档-CSDN博客 下载光盘 华为云地址:https://repo.huaweicloud.com/debian-cd/11.0.0/amd64/iso-cd/ 使用了debian11 教育版,比较有…...
SpringBoot——整合Thymeleaf模板
目录 模板引擎 新建一个SpringBoot项目 pom.xml application.properties Book BookController bookList.html 编辑 项目总结 模板引擎 模板引擎是为了用户界面与业务数据分离而产生的,可以生成特定格式的页面在Java中,主要的模板引擎有JSP&…...
电商推荐系统+电影推荐系统【虚拟机镜像分享】
电商推荐系统电影推荐系统【虚拟机镜像分享】 所有组件部署好的镜像下载(在下面),仅供参考学习。(百度网盘,阿里云盘…) 博主通过学习尚硅谷电商推荐电影推荐项目,将部署好的虚拟机打包成ovf文…...
(函数)判断素数(C语言)
一、运行结果; 二、源代码; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明素数判断函数; void prime(int number);int main() {//初始化变量值;int number 0;//获取用户输入的数据;printf(&quo…...
Articuler.Ai 技术深度解析:海量人脉匹配、数字足迹解析与高转化冷触达引擎
摘要Articuler.Ai 是一款面向商业人脉精准匹配与高效触达的 AI 引擎,核心定位为 “商业关系搜索引擎 智能触达工作台”,彻底重构传统关键词搜索失效背景下的 B2B 人脉连接逻辑。本文从9.8 亿级公开档案数据底座、语义匹配引擎架构、Playbook 深度解析技…...
SRWE终极指南:5分钟学会游戏窗口分辨率自定义技巧
SRWE终极指南:5分钟学会游戏窗口分辨率自定义技巧 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 想要在游戏中获得超高清截图,却受限于系统预设的分辨率?想要在窗口模式下享…...
2.2 本地文件读取
本章学习目标: 知道CSV、Excel、JSON三种文件分别怎么读、会遇到什么常见问题理解每种文件格式的“坑”在哪里,以及如何向AI描述解决方案学会用“人话”告诉AI你要做什么,让AI生成代码不需要记住任何函数名或参数,只需要知道“有什…...
量子纠错AI预解码器:加速表面码实时处理
1. 量子纠错与实时解码的挑战量子计算的核心难题之一是量子比特的脆弱性。与环境相互作用导致的退相干效应,使得量子信息在极短时间内就会发生不可逆的丢失。表面码(Surface Code)作为最具实用前景的量子纠错方案,通过将逻辑量子比…...
AI编程助手效率革命:结构化配置与提示词工程实战
1. 项目概述:一个为AI编程时代量身定制的开发者工具箱如果你和我一样,日常开发已经离不开像 Cursor 和 Claude 这样的 AI 编程助手,那你肯定也遇到过类似的困扰:每次开启一个新项目,或者在不同项目间切换时,…...
Muse:现代化多仓库管理工具,提升开发效率与协作体验
1. 项目概述:一个面向开发者的现代化代码库管理工具最近在和一些团队交流时,发现一个挺普遍的现象:大家手头的项目代码库越来越多,有的是自己维护的开源项目,有的是公司内部的核心业务模块,还有一堆实验性的…...
【其他】Obsidian笔记Remotely Save插件中国科技云数据胶囊 配置免费的笔记同步
目录 一 注册中国科技云数据胶囊 二 插件下载 & 配置 三 同步测试 一 注册中国科技云数据胶囊 【1】搜索“中国科技云”,找到“数据胶囊”选项,实名注册可以领取20G的容量: 【2】选择“新数据空间”,输入库的标题…...
惠来海康医院眼科母亲节:愿岁月温柔,护她眼底有光
惠来海康医院眼科母亲节:愿岁月温柔,护她眼底有光五月浅夏,暖意氤氲,当康乃馨的芬芳漫过街巷,母亲节便载着满心敬意如期而至。母亲,是岁月里最温柔的守望者,用一双眼眸,藏下对我们所…...
基于宏观通胀预测模型的利率预期重定价:华尔街降息路径为何出现系统性回撤?CPI成为关键校准变量
摘要:本文通过宏观通胀预测模型,结合利率预期曲线重定价算法与市场情绪迁移分析,对当前美通胀路径、CPI数据影响及华尔街降息预期变化进行系统性建模,分析利率政策预期从宽松交易向数据依赖模式切换的结构性原因。一、市场情绪迁移…...
告别手动刷新!用PowerShell脚本实现Windows下校园网自动重连(含任务计划设置)
告别手动刷新!用PowerShell脚本实现Windows下校园网自动重连(含任务计划设置) 每次开机都要手动登录校园网?网络突然断开还得重新输入账号密码?这些繁琐操作已经成为过去式。本文将手把手教你用PowerShell打造全自动校…...
