@Transactional注解 失效场景 及 解决版本

news/2024/4/15 13:59:38

文章目录

  • 失效场景
    • 1 数据库首先要支持事务:
    • 2.数据源没有配置事务管理器
    • 3. 没有被spring管理
    • 4.方法不是public
    • 5.@Transactional 注解属性 propagation 设置错误
    • 6.同一个类中方法调用,导致@Transactional失效
      • 方法一:新加一个service方法
      • 方法二:在该Service类中注入自己
      • 方法三:通过AopContext类
    • 7.通过try catch 把异常吃了
    • 8.异常类型错误【@Transactional 注解属性 rollbackFor 设置错误】
    • 9. 方法用final修饰
    • 10.多线程调用

失效场景

1 数据库首先要支持事务:

以mysql为例,其MyISAM引擎是不支持事务操作的,InnoDB才支持事务的引擎。从mysql5.5开始默认引擎是InnoDB,之前默认的是MyISAM

2.数据源没有配置事务管理器

3. 没有被spring管理

事务注解加到service层,如果没有@Service注解,这个类就不会被加载成一个Bean,那这个类就不会被spring管理,事务自然就失效了

4.方法不是public

根据spring官网,@Transactional只能用于public的方法上,否则事务不会生效,如果非要用在非public方法上,可以开启AspectJ代理模式。AspectJ使用加载时织入的方式,支持所有的pointcut,因此可以支持内部方法的事务设置

5.@Transactional 注解属性 propagation 设置错误

如注解配置的不支持事务,使用了Propagation。NOT_SUPPORTED:表示不易事务运行,当前若存在事务则挂起

6.同一个类中方法调用,导致@Transactional失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法a,a再调用本类的方法b(不论方法b是用public还是private修饰),但方法A没有声明注解事务,而b方法有。则外部调用方法a之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况?其实还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

 //@Transactional@GetMapping("/test")private void a(User user) throws Exception {userMapper.insert(user);b(user);}@Transactionalpublic void b(User user) throws Exception {doSomething();}

补充:

如果a方法同样加了 @Transactional注解,此时的事务是否能生效呢?

   上面的代码只是放开a方法上的@Transactional注解,此时a方法的事务依然是无法生效的。我们看到在事务方法a中,直接调用事务方法b。从前面介绍的内容可知,b方法拥有事务是因为spring aop生成了代理对象,但是这种方法直接调用了this对象的方法,所以a方法不会生成事务。

由此可见,在同一个类中的方法直接内部调用,会导致事务失效。

那么问题来了,这种场景如何解决呢?

方法一:新加一个service方法

这个方法非常简单,只需要新加一个Service方法,把@Transactional注解加到新的Service方法上,把需要事务执行的代码移到新方法中

​
​
@Servcie 
public class ServiceA { @Autowired prvate ServiceB serviceB; public void save(User user) { queryData1(); queryData2(); serviceB.doSave(user); } } @Servcie public class ServiceB { @Transactional(rollbackFor=Exception.class) public void doSave(User user) { userMapper.insert(user); b(user); } } ​​

方法二:在该Service类中注入自己

如果不想再新加一个Service类,在该Service类中注入自己也是一种选择。

​
​
​
@Servcie 
public class ServiceA { @Autowired prvate ServiceA serviceA; public void save(User user) { queryData1(); queryData2(); serviceA.doSave(user); } @Transactional(rollbackFor=Exception.class) public void doSave(User user) { userMapper.insert(user); b(user); } }

这种做法不会造成循环依赖问题,其实spring IOC内部的三级缓存保证了它,不会出现循环依赖问题。

方法三:通过AopContext类

引入依赖

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId>
</dependency>

开启AOP获取(在SpringBoot启动类或者配置类上加)

@EnableAspectJAutoProxy(exposeProxy = true)

在该Service类中使用AopContext.currentProxy()获取代理对象

@Servcie 
public class ServiceA { public void save(User user) { queryData1(); queryData2(); ((ServiceA)AopContext.currentProxy()).doSave(user); } @Transactional(rollbackFor=Exception.class) public void doSave(User user) { userMapper.insert(user); b(user); } } 

把异常吃了,然后又不抛出来,事务怎么回滚呢?

如果想要spring事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,则spring认为程序是正常的。

默默的说句,即使开发者没有手动捕获异常,但如果抛的异常不正确,spring事务也不会回滚。

因为spring事务,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),对于普通的Exception(非运行时异常),它不会回滚。

7.通过try catch 把异常吃了

8.异常类型错误【@Transactional 注解属性 rollbackFor 设置错误】

默认回滚的是RuntimeException,如果你想要触发其它的异常回滚,需要在注解上配置

@Transactional(rollbackFor = Exception.class)

9. 方法用final修饰

有时候,某个方法不想被子类重写,这时可以将方法定义成final的。普通方法这样定义没问题,但是如果将事务定义为final,这样会导致事务失效。

为什么呢?

   spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。但如果某个方法被final修饰了,那么在它的代理类中,就无法重写该方法,事务也就失效了。

注意:如果某个方法是static的,同样无法通过动态代理,变成事务方法。

10.多线程调用

在实际项目开发中,多线程的使用场景还是挺多的。如果spring事务用在多线程场景中,会有问题吗?

@Slf4j 
@Service 
public class UserService { @Autowired private UserMapper userMapper; @Autowired private RoleService roleService; @Transactional public void add(UserModel userModel) throws Exception { userMapper.insertUser(userModel); new Thread(() -> { roleService.doOtherThing(); }).start(); } 
} @Service 
public class RoleService { @Transactional public void doOtherThing() { System.out.println("保存role表数据"); } 
} 

从上面的例子中,我们可以看到事务方法add中,调用了事务方法doOtherThing,但是事务方法doOtherThing是在另外一个线程中调用的。

这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,add方法也回滚是不可能的。

如果看过spring事务源码的朋友,可能会知道spring的事务是通过数据库连接来实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。

我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。


http://www.ppmy.cn/news/1021988.html

相关文章

Ubuntu 20.04 中安装docker一键安装脚本

直接上脚本&#xff0c;依次执行如下命令即可 wget http://apollo-pkg-beta.bj.bcebos.com/docker_install.sh bash docker_install.shdocker install docker operation system Ubuntu 18.04 直接上脚本&#xff0c;依次执行如下命令即可 ways1 : wget https://github.com…

数据结构与算法基础-二进制运算性质及相关算法

二进制运算性质及相关算法 1 几个与码相关的术语 2 位运算概述 3 位运算符的运算率 1. 交换律 2. 结合律 3. 等幂律 4.零律 4 位运算的高级操作 1 几个与码相关的术语 原码 : 以二进制形式在计算机中存储,数值前直接加一符号位的表示法 1表示负数 0 表示正数 反码 : …

bootloader跳转APP注意事项

在gd32f427 时跳转异常 参考文章&#xff1a; https://club.rt-thread.org/ask/question/425321.html%20https:/club.rt-thread.org/ask/question/eab19452583b5959.html https://club.rt-thread.org/ask/question/eab19452583b5959.html 关闭全部中断&#xff0c;并且清除中…

力扣初级算法(旋转矩阵)

力扣初级算法(旋转矩阵) 每日一算法&#xff1a;旋转矩阵 学习内容&#xff1a; 1.问题&#xff1a; 给你一幅由 N N 矩阵表示的图像&#xff0c;其中每个像素的大小为 4 字节。请你设计一种算法&#xff0c;将图像旋转 90 度。 不占用额外内存空间能否做到&#xff1f; 2.…

人工智能行业岗位一览

人工智能行业的岗位薪资高、待遇好、涨薪快已经是公开的事实&#xff0c;那么在人工智能行业中具体有哪些职业岗位呢&#xff1f;对于普通人来说&#xff0c;想要入行人工智能又有哪些机会呢&#xff1f; 下面是人工智能领域中的一部分职业岗位&#xff0c;随着技术的不断发展&…

uniapp uni-datetime-picker 日期和光标靠右

如果想在uni-datetime-picker组件中将日期和光标靠右&#xff0c;您可以使用自定义样式来实现。首先&#xff0c;您需要在页面的样式文件中定义一个类&#xff0c;用于定制uni-datetime-picker组件的样式。例如&#xff0c;你可以在App.vue或者页面的样式文件中添加以下代码&am…

苍穹外卖day11笔记

今日首先介绍前端技术Apache ECharts&#xff0c;说明后端需要准备的数据&#xff0c;然后讲解具体统计功能的实现&#xff0c;包括营业额统计、用户统计、订单统计、销量排名。 一、ECharts 是什么 ECharts是一款基于 Javascript 的数据可视化图表库。我们用它来展示图表数…

CV大一统模型的第一步!Segment Anything Model 最全解读!

Datawhale干货 作者&#xff1a;崔腾松&#xff0c;Datawhale成员 前言 Meta 开源万物可分割 AI 模型&#xff1a;segment anything model (SAM)。 本文列举了一些资料&#xff0c;并从SAM的功能介绍、数据集、数据标注、图像分割方法介绍&#xff0c;研发思路以及对未来的展望…

strlen和sizeof的区别

strlen 和 sizeof 是两个在C语言中用于处理字符串和数据类型大小的操作符&#xff0c;它们有一些重要的区别&#xff1a; 1. **strlen**&#xff1a; - strlen 是一个函数&#xff0c;用于计算以 null 终止的字符串的实际长度&#xff0c;即字符数&#xff08;不包括 null …

mysql基础之触发器的简单使用

1.建立学生信息表 -- 触发器 -- 建立学生信息表 create table s1(id int unsigned auto_increment,name varchar(30),score tinyint unsigned,dept varchar(50),primary key(id) );2.建立学生补考信息表 -- 建立学生补考信息表 create table s2 like s1;3.建立触发器&#xf…

十二种排序算法(附动图)

排序算法 一、基本介绍 ​ 排序算法比较基础&#xff0c;但是设计到很多计算机科学的想法&#xff0c;如下&#xff1a; ​ 1、比较和非比较的策略 ​ 2、迭代和递归的实现 ​ 3、分而治之思想 ​ 4、最佳、最差、平均情况时间复杂度分析 ​ 5、随机算法 二、排序算法的分类 …

Codeforces Round 828 (Div. 3)E题题解

文章目录 [Divisible Numbers (easy version)](https://codeforces.com/contest/1744/problem/E1)问题建模问题分析代码 [ Divisible Numbers (hard version)](https://codeforces.com/contest/1744/problem/E2)问题建模问题分析1.根据简单版本分析所求2.方法1通过因数分解得到…

【数学建模】--主成分分析

本讲将介绍主成分分析&#xff08;Principal Component Analysis&#xff0c;PCA&#xff09;&#xff0c;主成分分析是一种降维算法&#xff0c;它能将多个指标转换为少数几个主成分&#xff0c;这些主成分是原始变量的线性组合&#xff0c;且彼此之间互不相关&#xff0c;其能…

verilog 实现异步fifo

理论知识参考 异步FIFO_Verilog实现_verilog实现异步fifo_Crazzy_M的博客-CSDN博客 代码 /* 位宽8bit, 位深8 */ module async_fifo#(parameter FIFO_DEPTH 8,parameter FIFO_WIDTH 8 ) (input rst_n,input wr_clk,input wr_en,input [FIFO_WIDTH - 1:0…

k8s资源管理方法详解(陈述式、声明式)

目录 一&#xff1a;陈述式资源管理方法 二&#xff1a; 基本信息查看 1、查看信息 2、创建 3、删除 4、service 的 type 类型 三&#xff1a;项目实例 1、创建 kubectl create命令 2、发布 kubectl expose命令 3、在 node 节点上操作&#xff0c;查看负载均衡端…

Gradio入门,并搭个鸡兔同笼问题小应用,附源码(MindOpt)

应用链接&#xff1a; https://979427749bc9ceec34.gradio.live 是公开访问链接&#xff0c;3天有效。在modelscope中的创空间怎么长期发布我还在研究。后面补上。 应用图如下&#xff0c;源代码见正文。 知道Gradio AI大模型快速生成应用的工具&#xff0c; 在Huggingface …

GRPC-连接池-GPT

gRPC Dart 管理优化 User grpc for dart 如何统一管理多个Client的创建和销毁&#xff0c;给我一个最优解 ChatGPT 对于在Dart中使用gRPC创建和销毁多个Client的统一管理&#xff0c;您可以使用一个单例模式的管理类来实现最优解。 首先&#xff0c;创建一个管理类&#xff0c…

不侵入代码的rem适配,支持桌面缩放,vue2的适配方案

此方式不侵入代码&#xff0c;自动把px单位转换成rem单位 首先安装postcss-pxtorem5.1.1 yarn add postcss-pxtorem5.1.1 npm install postcss-pxtorem5.1.1 --save 项目根目录新建 postcss.config.js module.exports {plugins: {postcss-pxtorem: {rootValue: 14,propList…

多线程的实现方式Thread、Runnable、Callable

1.并发和并行 并发&#xff1a;在同一时刻&#xff0c;有多个指令在单个CPU上交替执行。 并行&#xff1a;在同一时刻&#xff0c;有多个指令在多个CPU上同时执行 2.多线程的实现方式 2.1 继承Thread类实现方式 2.2 实现Runnable接口的实现方式 2.3 利用Callable接口和Futur…

FFmpeg常见命令行(四):FFmpeg流媒体

前言 在Android音视频开发中&#xff0c;网上知识点过于零碎&#xff0c;自学起来难度非常大&#xff0c;不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》&#xff0c;结合我自己的工作学习经历&#xff0c;我准备写一个音视频系列blog。本文是音视频系…
最新文章