Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >@Transactional 你真的用对了吗?

@Transactional 你真的用对了吗?

原创
作者头像
别惹CC
发布于 2025-01-14 02:28:40
发布于 2025-01-14 02:28:40
1480
举报

场景

我们在日常开发中几乎经常会使用@Transactional对方法或者接口进行事务管理,毕竟声明式事务这么方便,谁不爱呢,哈哈!但是这个注解的实现原理我们需要搞清楚才能避免“踩坑”。本篇文章就是针对一个使用场景来讨论的,希望可以帮助大家避免在开发中犯下这个错。

某天我在做自己的需求时,发现有一个同事写的老接口(Ps:此处真的是我有一个同事😶)里的一行代码IDE有个警告提示,本来也没在意,以为就是个格式检查,但是瞄了一眼,发现不对劲,提示的是:

"@Transactional 自调用(实际上是目标对象内的方法调用目标对象的另一个方法)在运行时不会导致实际的事务。"

这和事务扯上关系了,那我当然要好好掰头一下,于是我看了一下他的代码,大概是这样(不能暴露业务代码,我搞个伪代码给大家看看):

代码语言:java
AI代码解释
复制
@Override
public void updateA(List<Things> thingList) {
  ...
  updateB(thingList);
}                          

在这个代码里,updateA 方法里调用了 updateB 方法,它们在各自的接口上都@Transactional注解,但是这个调用又是在当前同一个类下进行的,所以不会开启新的事务,进而也就有了IDE的这个提示。

了解风险

既然已经发现了这个问题出现的场景,那么我们也需要明确这样使用有什么风险呢?其实从IDE的警告来看就已经十分清晰了,关注最后一句“在运行时不会导致实际的事务”,意思就是有一个方法的事务并不会生效,那么就有数据不一致的风险了。

可能有同学还是有点懵,我举个例子:假设你有一个事务,它包含两个操作:操作A和操作B。如果操作A成功,但操作B失败,那么操作A的结果将被回滚,但操作B由于声明式事务未生效,即未被事务管理,那么它将不会被回滚,但这样如果两个操作涉及到同一数据的处理就会造成不一致的问题了。

解决方案

当然了,作为一个有点东西的专业开发,我们不能只提出问题,而不解决问题嘛,针对这个场景,我们可以有下面三种方案来解决问题:

方案一:移动大法好

这里我们不提在controller层注入service、service层注入mapper之类的办法,满足有的同学不想动业务代码这一需求,来解决这一问题。既然问题的根因在于Spring 的事务管理默认是基于代理的,只有通过代理对象调用的方法才会被 Spring 的事务管理器捕获并处理。 那么就把其中一个方法在另一个服务类中声明,然后在当前类中注入并调用。这样,它的调用就会被Spring的事务管理器捕获,并在新的事务中运行。

方案二:巧借AOP

使用AopContext.currentProxy()来获取当前的代理对象,然后通过这个代理对象来调用方法。这样,被调用的方法就会被Spring的事务管理器捕获,并在新的事务中运行。但是,这种方法需要在Spring的配置中启用exposeProxy属性。

代码语言:java
AI代码解释
复制
(YourService) 
AopContext.currentProxy().updateB(thingList);

方案三:IOC容器来帮你

使用ApplicationContext来获取当前的Bean,然后通过这个Bean来调用方法。这样,被调用的方法就会被Spring的事务管理器捕获,并在新的事务中运行。但是,这种方法需要在你的类中注入ApplicationContext

代码语言:java
AI代码解释
复制
@Autowired
private ApplicationContext applicationContext;

...

applicationContext.getBean("yourService")).updateB(thingList);

注意

以上三种方法的作用是相同的,都会让每个方法都会被其自己的事务管理。具体来说,当你通过Spring的代理对象或者Bean来调用一个方法时,Spring的事务管理器会为这个方法开启一个新的事务。这个新的事务是独立于当前事务的,也就是说,它有自己的事务边界,可以独立地提交或者回滚。</br>

所以,如果A和B操作都对数据库中的同一行数据进行操作,并且它们是在两个不同的事务中执行的,那么如果B操作失败并回滚,A操作不会被回滚。这可能会导致数据的一致性问题。</br>

因此,最终想要两个方法被一个事务管理,还是得通过在一个方法中调用A和B操作来实现这一点,并在这个方法上使用@Transactional注解。

代码语言:java
AI代码解释
复制
@Transactional
public void updateData() {
    operationA();
    operationB();
}

小结

方法是死的,人是活的。大家还是要根据自己实际的业务场景选择合适的方式,当然,尽量还是避免出现这种自调用的事务操作!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux
drwxr-xr-x 3 root root 4096 7月 14 10:23 text
化羽羽
2022/10/28
5820
Linux基础语法
linux诞生了这么多年,以前还喊着如何能取代windows系统,现在这个口号已经小多了,任何事物发展都有其局限性都有其天花板。就如同在国内再搞一个社交软件取代腾讯一样,想想而已基本不可能,因为用户已经习惯于使用微信交流,不是说技术上实现不了解而是老百姓已经习惯了,想让他们不用,即使他们自己不用亲戚朋友还是要用,没有办法的事情。
全栈程序员站长
2022/08/30
1.5K0
Linux基础语法
狂神说Linux_狂神说java
在Linux中我们可以使用ll或者ls –l命令来显示一个文件的属性以及文件所属的用户和组,如:
全栈程序员站长
2022/09/21
4720
狂神说Linux_狂神说java
【总结】LINUX常见易错命令操作
本文讲解LINUX下用户/用户组,文件属性及修改,SUDO免密登录,SSH互信免密登录,VIM,history等命令操作实践。
辉哥
2021/05/27
1.1K0
Linux学习(三)——vi/vim 的使用
vi/vim 共分为三种模式,分别是命令模式(Command mode),**输入模式(Insert mode)和底线命令模式(**Last line mode)。
传说之下的花儿
2023/04/16
4660
Linux学习(三)——vi/vim 的使用
linux的vi命令详解_centos7 vi命令
  vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器.由于 对Unix及Linux系统的任何版本,vi编辑器是完全相同的,Vi是Linux中最基本的文本编辑器。
全栈程序员站长
2022/11/08
10.5K0
linux的vi命令详解_centos7 vi命令
高手 都喜欢用vim编辑器
现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。
互扯程序
2018/07/30
6570
高手 都喜欢用vim编辑器
Linux笔记
sed d 删除行 [address]d sed a/i 插入 [address]a(或 i)\新文本内容
菜鸟雷
2020/10/23
1.5K0
Linux笔记
Linux命令2
在Unix/Linux中添加用户账号可以使用adduser或useradd命令,因为adduser命令是指向useradd命令的一个链接,因此,这两个命令的使用格式完全一样。
周小董
2019/03/25
1.4K0
Linux命令2
vi/vim 常用命令总结
目录 Linux vi/vim编辑 vim键盘图 vim的三种模式 命令模式、输入模式、输出模式 vim使用实例 vi/vim按键说明 第一部分:一般模式可用的光标移动、复制粘贴、搜索替换等 第二部分:一般模式切换到编辑模式的可用的按钮说明 第三部分:一般模式切换到指令行模式可用的按钮说明 Linux vi/vim编辑 vim键盘图 vim的三种模式 命令模式、输入模式、输出模式 三种模式的切换 用户刚启动vim就是命令模式! i、a、o切换到输入模式 输入模式下摁Esc按键切换到命令模式 命令模式输
HammerZe
2022/05/09
6040
vi/vim 常用命令总结
Vim 编辑器与 Python 命令脚
在每次运行 Vim 编辑器时,默认进入命令模式,此时需要先切换(i)到输入模式后再进行文档编写工作,而每次在编写完文档后需要先返回(ESC)命令模式,然后再进入(:)末行模式,执行文档的保存(wq)或退出(q!)操作。
py3study
2020/01/02
6210
❤️肝下25万字的《决战Linux到精通》笔记,你的Linux水平将从入门到入魔❤️【建议收藏】
因为AT&T(通用电气)的政策改变,在Version 7 Unix推出之后,发布新的使用条款,将UNIX源代码私有化,在大学中不再能使用UNIX源代码。Andrew S. Tanenbaum(塔能鲍姆)教授为了能在课堂上教授学生操作系统运作的实务细节,决定在不使用任何AT&T的源代码前提下,自行开发与UNIX兼容的操作系统,以避免版权上的争议。他以小型UNIX(mini-UNIX)之意,将它称为MINIX。
全栈程序员站长
2022/09/05
2.3K0
❤️肝下25万字的《决战Linux到精通》笔记,你的Linux水平将从入门到入魔❤️【建议收藏】
Vim 快速入门
在 Linux 下最常使用的文本编辑器就是 vi 或者 vim 了,如果能很好掌握这个编辑器,非常有利于我们更好的在 Linux 下面进行编程开发。
kbsc13
2019/08/16
1.2K0
看完这篇文章,你的Linux基础就差不多了
这篇文章基于传智播客的2016年的gitbook资料和视频资料,同时也融合了2018年的视频和课件资料中的一些内容,即以2016年的资料为蓝本,2018年的资料为辅助编写的。
用户7657330
2020/08/14
8550
看完这篇文章,你的Linux基础就差不多了
Linux文件管理
用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。
星陨1357
2023/03/14
2.7K0
Linux文件管理
linux基础
1.目录 /:根目录,一般根目录下只存放目录,在Linux下有且只有一个根目录。所有的东西都是从这里开始。当你在终端里输入“/home”,你其实是在告诉电脑,先从/(根目录)开始,再进入到home目录。 /bin、/usr/bin: 可执行二进制文件的目录,如常用的命令ls、tar、mv、cat等。 /boot:放置linux系统启动时用到的一些文件,如Linux的内核文件:/boot/vmlinuz,系统引导管理器:/boot/grub。 /dev:存放linux系统下的设备文件,访问该目录下某个文件,相
zhang_derek
2018/04/11
2K0
linux基础
LINUX中常用操作命令
LINUX中常用操作命令 Linux简介及Ubuntu安装 常见指令 系统管理命令 打包压缩相关命令 关机/重启机器 Linux管道 Linux软件包管理 vim使用 用户及用户组管理 文件权限管理 ---- Linux简介及Ubuntu安装 Linux,免费开源,多用户多任务系统。基于Linux有多个版本的衍生。RedHat、Ubuntu、Debianaa 安装VMware或VirtualBox虚拟机。具体安装步骤,找百度。 再安装Ubuntu
Angel_Kitty
2018/04/09
1.5K0
LINUX中常用操作命令
大数据学习之Linux基础
如果c盘有ssd,建议将虚拟机安装在c盘, 因为这样安装以后打开虚拟机更快. 同时, 建议新建一个目录, 来存放虚拟机
时间静止不是简史
2020/07/24
1.4K0
大数据学习之Linux基础
Linux中基本命令 原
3、man 命令:enter按行翻,空格按页翻;HOME/END键可以快速到首页/尾页(SecureCRT中无效);查找按 /要查找的内容,查找 下一个/上一个:按n/N;退出按q。
云飞扬
2019/03/13
7470
Linux命令
ls是英文单词list的简写,其功能为列出目录的内容,是用户最常用的命令之一,它类似于DOS下的dir命令。
py3study
2020/01/16
3.4K0
相关推荐
Linux
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文