Spring 事务不生效的几种场景

Spring 事务不生效的几种场景

详细内容参考以下链接,这个链接是原文

spring 事务不生效的15中场景

非原创。

以下内容只是为了学习,加深印象,仅作为个人学习笔记, 请支持原创,内容请点击 spring 事务不生效的15中场景

-------------------------------------------------------------------------------分割线-----------------------------------------------------------------------

整理如下几种Spring事务失效的场景

在这里插入图片描述

1、service类没有被Spring管理

//@Service (注释了@Service 或者 忘记添加注解)
public class xxxImpl implements xxxService {@Autowiredprivate xxMapper xxMapper;@Autowiredprivate yyMapper yyFlowMapper;@Transactionalpublic void addTianLuo(Domain domain) {//do somethingxxMapper.save(domain);//do other somethingyyFlowMapper.saveFlow(domain);}
}
  • 事务不生效的原因:上面例子中, @Service注解注释之后,spring事务(@Transactional)没有生效,因为Spring事务是由AOP机制实现的,也就是说从Spring IOC容器获取bean时,Spring会为目标类创建代理,来支持事务的。但是@Service被注释后,你的service类都不是spring管理的,那怎么创建代理类来支持事务呢
  • 解决方案:加上@Service注解。

2.没有在Spring配置文件中启用事务管理器

以下案例只针对非Spring Boot 项目

@Configuration
public class AppConfig {// 没有配置事务管理器
}@Service
public class MyService {@Transactionalpublic void doSomething() {// ...}
}
  • 事务不生效的原因:没有在AppConfig中配置事务管理器,因此Spring无法创建事务代理对象,导致事务不生效。即使在MyService中添加了@Transactional注解,该方法也不会被Spring管理的事务代理拦截。
  • 解决方案:为了解决这个问题,应该在AppConfig中配置一个事务管器。例如:
@Configuration
public class AppConfig {@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}
}@Service
public class MyService {@Transactionalpublic void doSomething() {// ...}
}

3、事务方法被final、static、private关键字修饰

@Service
public class xxImpl implements xxxService {@Autowiredprivate xxMapper xxMapper;@Transactionalpublic final void addTianLuo(Domain domain) {xxMapper.save(domain); xxMapper.saveOther(domain);}
}
  • 事务不生效的原因:如果一个方法被声明为final或者static,则该方法不能被子类重写,也就是说无法在该方法上进行动态代理,这会导致Spring无法生成事务代理对象来管理事务。
  • 解决方案addTianLuo事务方法不要用final修饰或者static修饰。

4、同一个类中,方法内部调用

@Service
public class xxServiceImpl implements xxService {@Autowiredprivate xxMapper xxMapper;public void addTianLuo(Domain domain){// 调用内部的事务方法this.executeAddMethod(domain);}@Transactionalpublic void executeAddMethod(Domain domain) {xxMapper.save(domain); xxMapper.saveOther(domain);}
}
  • 事务不生效的原因: 事务是通过Spring AOP代理来实现的,而在同一个类中,一个方法调用另一个方法时,调用方法直接调用目标方法的代码,而不是通过代理类进行调用。即以上代码,调用目标executeAddTianLuo方法不是通过代理类进行的,因此事务不生效。
  • 解决方案:可以新建多一个类,让这两个方法分开,分别在不同的类中, 当然也可以注入自己,然后调用,这种方法不太优雅。

总结:

以上几种类型都是使用了声明式的@Transaction事务注解,从spring Aop 为事务类生成 代理对象的角度出发,当类没有被IOC管理、使用了不当的修饰符、没有使用spring 生产的代理对象的方法(内部方法直接调用)等导致事务无法生效。

6、数据库的存储引擎不支持事务

​ Spring事务的底层,还是依赖于数据库本身的事务支持。在MySQL中,MyISAM存储引擎是不支持事务的,InnoDB引擎才支持事务。因此开发阶段设计表的时候,确认你的选择的存储引擎是支持事务的

7、配置错误的 @Transactional 注解

@Transactional(readOnly = true)
public void updateUser(User user) {userDao.updateUser(user);
}
  • 事务不生效的原因:虽然使用了@Transactional注解,但是注解中的readOnly=true属性指示这是一个只读事务,因此在更新User实体时会抛出异常。
  • 解决方案:将readOnly属性设置为false,或者移除了@Transactional注解中的readOnly属性。

8.事务超时时间设置过短

@Transactional(timeout = 1)
public void doSomething() {//...
}

事务不生效的原因:在上面的例子中,timeout属性被设置为1秒,这意味着如果事务在1 秒内无法完成,则报事务超时了

9. 使用了错误的事务传播机制

@Service
public class xxServiceImpl {@Autowiredprivate XXMapper xxMapper;@Autowiredprivate XXFlowMapper xxFlowMapper;@Transactional(propagation = Propagation.NOT_SUPPORTED)public  void doInsertTianluo(XX xx) throws Exception {xxMapper.save(xx);xxFlowMapper.saveFlow(xx);}
}
  • 事务不生效的原因Propagation.NOT_SUPPORTED传播特性不支持事务。
  • 解决方案:选择正确的事务传播机制。

帮大家复习一下,Spring提供了七种事务传播机制。它们分别是:

  • REQUIRED(默认):如果当前存在一个事务,则加入该事务;否则,创建一个新事务。该传播级别表示方法必须在事务中执行。
  • SUPPORTS:如果当前存在一个事务,则加入该事务;否则,以非事务的方式继续执行。
  • MANDATORY:如果当前存在一个事务,则加入该事务;否则,抛出异常。
  • REQUIRES_NEW:创建一个新的事务,并且如果存在一个事务,则将该事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在一个事务,则将该事务挂起。
  • NEVER:以非事务方式执行操作,如果当前存在一个事务,则抛出异常。
  • NESTED:如果当前存在一个事务,则在嵌套事务内执行。如果没有事务,则按REQUIRED传播级别执行。嵌套事务是外部事务的一部分,可以在外部事务提交或回滚时部分提交或回滚。

10. rollbackFor属性配置错误

@Service
public class XXerviceImpl implements XXService {@Autowiredprivate XXMapper xxMapper;@Autowiredprivate XXFlowMapper xxFlowMapper;@Transactional(rollbackFor = Error.class)public void addTianLuo(XX xx) {//xxMapper.save(xx);//xxFlowMapper.saveFlow(xx);//模拟异常抛出throw new Exception();}
}
  • 事务不生效的原因: 其实rollbackFor属性指定的异常必须是Throwable或者其子类。默认情况下,RuntimeExceptionError两种异常都是会自动回滚的。但是因为以上的代码例子,指定了rollbackFor = Error.class,但是抛出的异常又是Exception,而Exception和Error没有任何什么继承关系,因此事务就不生效。

  • 解决方案rollbackFor属性指定的异常与抛出的异常匹配。

在这里插入图片描述

11.事务注解被覆盖导致事务失效

public class MyService {@Autowiredprivate MyRepository myRepository;@Transactionalpublic void doSomething(String data) {myRepository.save(data);}
}public class MyTianluoService extends MyService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void doSomething(String data) {super.doSomething(data);}
}
  • 事务失效的原因MyTianluoServiceMyService的子类,并且覆盖了doSomething()方法。在该方法中,使用了不同的传播行为(REQUIRES_NEW)来覆盖父类的@Transactional注解。在这种情况下,当调用MyTianluoServicedoSomething()方法时,由于子类方法中的注解覆盖了父类的注解,Spring框架将不会在父类的方法中启动事务。因此,当MyRepositorysave()方法被调用时,事务将不会被启动,也不会回滚。这将导致数据不一致的问题,因为在MyRepositorysave()方法中进行的数据库操作将不会回滚。

12.嵌套事务的坑

@Service
public class TianLuoServiceInOutService {@Autowiredprivate TianLuoFlowService tianLuoFlowService;@Autowiredprivate TianLuoMapper tianLuoMapper;@Transactionalpublic void addTianLuo(TianLuo tianluo) throws Exception {tianLuoMapper.save(tianluo);// 因tianLuoFlowService.saveFlow事务回滚,该方法事务也回滚tianLuoFlowService.saveFlow(tianluo);}
}@Service
public class TianLuoFlowService {@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;@Transactional(propagation = Propagation.NESTED)public void saveFlow(TianLuo tianLuo) {tianLuoFlowMapper.save(tianLuo);throw new RuntimeException(); //继续往上抛}
}

以上代码使用了嵌套事务,如果saveFlow出现运行时异常,会继续往上抛,到外层addTianLuo的方法,导致tianLuoMapper.save也会回滚啦。如果不想因为被内部嵌套的事务影响,可以用try-catch包住,如下:

    @Transactionalpublic void addTianLuo(TianLuo tianluo) throws Exception {tianLuoMapper.save(tianluo);try {tianLuoFlowService.saveFlow(tianluo);} catch (Exception e) {log.error("save tian luo flow fail,message:{}",e.getMessage());}}

13. 事务多线程调用

@Service
public class TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowService tianLuoFlowService;@Transactionalpublic void addTianLuo(TianLuo tianluo) {//保存tianluo数据库记录tianLuoMapper.save(tianluo);//多线程调用new Thread(() -> {tianLuoFlowService.saveFlow(tianluo);}).start();}
}@Service
public class TianLuoFlowService {@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;@Transactionalpublic void save(TianLuo tianLuo) {tianLuoFlowMapper.saveFlow(tianLuo);}
}

事务不生效原因:这是因为Spring事务是基于线程绑定的,每个线程都有自己的事务上下文,而多线程环境下可能会存在多个线程共享同一个事务上下文的情况,导致事务不生效。

在Spring事务管理器中,通过TransactionSynchronizationManager类来管理事务上下文。TransactionSynchronizationManager内部维护了一个ThreadLocal对象,用来存储当前线程的事务上下文。在事务开始时,TransactionSynchronizationManager会将事务上下文绑定到当前线程的ThreadLocal对象中,当事务结束时,TransactionSynchronizationManager会将事务上下文从ThreadLocal对象中移除。

14.异常被捕获并处理了,没有重新抛出

@Service
public class TianLuoServiceImpl implements TianLuoService {@Transactionalpublic void addTianLuo(TianLuo tianluo) {try {...} catch (Exception e) {log.error("add TianLuo error,id:{},message:{}", tianluo.getId(),e.getMessage());}}
}
  • 事务不生效的原因: 事务中的异常已经被业务代码捕获并处理,而没有被正确地传播回事务管理器,事务将无法回滚。我们可以从spring源码(TransactionAspectSupport这个类)中找到答案:

  • 解决方案:在spring事务方法中,当我们使用了try-catch,如果catch住异常,记录完异常日志什么的,一定要重新把异常抛出来,正例如下:

15.手动抛了别的异常

以上面的第10点比较类似

@Service
public class TianLuoServiceImpl implements TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;@Transactionalpublic void addTianLuo(TianLuo tianluo) throws Exception {//保存tianluo数据库记录tianLuoMapper.save(tianluo);//保存tianluo流水数据库记录tianLuoFlowMapper.saveFlow(tianluo);throw new Exception();}
}

与上面的第10点比较类似

  • 失效的原因:上面的代码例子中,手动抛了Exception异常,但是是不会回滚的,因为Spring默认只处理RuntimeException和Error,对于普通的Exception不会回滚,除非,用rollbackFor属性指定配置。
  • 解决方案:添加属性配置@Transactional(rollbackFor = Exception.class)

注解为事务范围的方法中,事务的回滚仅仅对于unchecked的异常有效。对于checked异常无效。也就是说事务回滚仅仅发生在,出现RuntimeException或Error的时候。通俗一点就是:代码中出现的空指针等异常,会被回滚。而文件读写、网络超时问题等,spring就没法回滚了。

非原创,详细内容参考以下链接,这个链接是原文:

spring 事务不生效的15中场景

此内容只是为了学习,加深印象,仅作为个人学习笔记, 请支持原创, 请支持原创,内容请点击 spring 事务不生效的15中场景

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

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

相关文章

链动2+1模式:白酒产品的营销新策略

链动21模式是一种创新的营销模式,结合白酒产品更能发挥其优势。该模式通过独特的身份晋升和奖励机制,快速建立销售渠道,提高用户粘性。 一、核心机制 身份晋升机制:用户购买指定499白酒产品后成为代理,再邀请两位用户…

新风机如何联动?

数据中心的运行会产生大量的热量,因为其中包含了大量的服务器、存储设备以及网络设备等,它们需要消耗大量的电力来进行计算和数据处理。为了保证这些设备运行的稳定性和性能,数据中心必须维持适宜的温度和湿度。 新风系统可以在数据中心中起到…

小米澎湃OS发布,雷军小米的“统一”大棋局

千呼万唤始出来。2023年10月26日,小米澎湃OS终于揭开面纱。 雷军在主题为“跨越时刻”的发布会上,正式发布了小米澎湃OS。面对这款历时七年打造的全新操作系统,雷军难掩兴奋,他感慨道:“我心澎湃”。 小米新操作系统取…

微信小程序项目案例之导游证考试刷题小程序

前言 很多计算机专业的同学在做毕设选题时不知道该如何选题,有的同学是已经选择了要开发一款小程序,但是又不知道开发哪类小程序。本篇将为大家介绍一个小程序的开发方向,考试刷题类小程序是目前比较火的小程序项目之一,在小程序…

CB2-2CARD的openSUSE远程SSH登录提示优化

CB2-2CARD的openSUSE远程SSH登录提示优化 1. 源由2. 优化内容2.1 去掉Password/banner前后的prompts提示语句2.2 增加logo登录界面2.3 增加系统运行情况简单汇报2.4 增加banner 3. 优化效果 1. 源由 之前运行的CB2-2CARD的openSUSE安装&NAS环境配置服务器已经运行也有段时…

(c语言进阶)字符串函数、字符分类函数和字符转换函数

一.求字符串长度 1.strlen() (1)基本概念 头文件&#xff1a;<string.h> (2)易错点&#xff1a;strlen()的返回值为无符号整形 #include<stdio.h> #include<string.h> int main() {const char* str1 "abcdef";const char* str2 "bbb&q…

审核 Microsoft SQL Server 日志

手动审核数据库活动是一项艰巨的任务&#xff0c;有效完成审计的最佳方法是使用简化和自动化数据库监控的综合解决方案&#xff0c;该解决方案还应使数据库管理员能够监控、跟踪和即时识别任何操作问题的根本原因&#xff0c;并实时检测对机密数据的未经授权的访问。 什么是 S…

前端面试 面试多起来了

就在昨天 10.17 号,同时收到了三个同学面试的消息。他们的基本情况都是双非院校本科、没有实习经历、不会消息中间件和 Spring Cloud 微服务,做的都是单体项目。但他们投递简历还算积极,从今年 9 月初就开始投递简历了,到现在也有一个多月了。 来看看,这些消息。 为…

中文编程工具免费版下载,中文开发语言工具免费版下载

中文编程工具免费版下载&#xff0c;中文开发语言工具免费版下载 中文编程工具开发的实际部分案例如下图 编程系统化课程总目录及明细&#xff0c;点击进入了解详情。 https://blog.csdn.net/qq_29129627/article/details/134073098?spm1001.2014.3001.5502

RabbitMQ消息中间件

一、初始MQ 首先了解一下微服务间通讯有同步和异步两种方式&#xff1a;- 同步通讯&#xff1a;是指两个或多个系统在进行信息交换时&#xff0c;必须在同一时刻进行操作 - 异步通讯&#xff1a;是指两个或多个系统之间的通讯方式&#xff0c;其中发送方和接收方不是在同一时刻…

星环科技分布式向量数据库Transwarp Hippo正式发布,拓展大语言模型时间和空间维度

随着企业、机构中非结构化数据应用的日益增多以及AI的爆发式增长所带来的大量生成式数据&#xff0c;所涉及的数据呈现了体量大、格式和存储方式多样、处理速度要求高、潜在价值大等特点。但传统数据平台对这些数据的处理能力较为有限&#xff0c;如使用文件系统、多类不同数据…

Vue显示FFmpeg推的流

零、环境安装 小弟的另一篇文章&#xff1a; FFmpeg和rtsp服务器搭建视频直播流服务-CSDN博客 一、FFmpeg推流 1、拉取rtsp摄像头流 sudo ffmpeg -f v4l2 -input_format mjpeg -i /dev/video0 -c:v copy -f rtsp rtsp://10.168.3.196:8554/mystream2、推视频的rtmp流 sudo ffm…

python数据可视化

内容主要介绍了python模块matplotlib即seaborn数据可视化 matplotlib模块通过import matplotlib.pyplot as plt生成图形&#xff0c;如生成图形没展示&#xff0c;可调用plt.show()方法展示图形&#xff1b; 对于颜色属性设置&#xff0c;既可以使用十六进制颜色表达(#7777aa…

图像特征Vol.1:计算机视觉特征度量|第一弹:【纹理区域特征】

目录 一、前言二、纹理区域度量2.1&#xff1a;边缘特征度量2.2&#xff1a;互相关和自相关特征2.3&#xff1a;频谱方法—傅里叶谱2.4&#xff1a;灰度共生矩阵(GLCM)2.5&#xff1a;Laws纹理特征2.6&#xff1a;局部二值模式&#xff08;LBP&#xff09; 一、前言 &#x1f…

【网络安全 --- 任意文件上传漏洞靶场闯关 6-15关】任意文件上传漏洞靶场闯关,让你更深入了解文件上传漏洞以及绕过方式方法,思路技巧

一&#xff0c;工具资源下载 百度网盘资源下载链接地址&#xff1a; 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固&#xff0c;支持教育网加速&#xff0c;支持手机端。注册使用百度网盘即可享受免费存储空间https://pan…

Generative AI 新世界 | Falcon 40B 开源大模型的部署方式分析

在上期文章&#xff0c;我们探讨了如何在自定义数据集上来微调&#xff08;fine-tuned&#xff09;模型。本期文章&#xff0c;我们将重新回到文本生成的大模型部署场景&#xff0c;探讨如何在 Amazon SageMaker 上部署具有 400 亿参数的 Falcon 40B 开源大模型。 亚马逊云科技…

干货!数字IC后端入门学习笔记

很多同学想要了解IC后端&#xff0c;今天大家分享了数字IC后端的学习入门笔记&#xff0c;供大家学习参考。 很多人对于后端设计的概念比较模糊&#xff0c;需要做什么也都不甚清楚。 有的同学认为就是跑跑 flow、掌握各类工具。 事实上&#xff0c;后端设计的工作远不止于此。…

【C++笔记】C++多态

【C笔记】C多态 一、多态的概念及实现1.1、什么是多态1.2、实现多态的条件1.3、实现继承与接口继承1.4、多态中的析构函数1.5、抽象类 二、多态的实现原理 一、多态的概念及实现 1.1、什么是多态 多态的概念&#xff1a; 在编程语言和类型论中&#xff0c;多态&#xff08;英…

DNS 域名解析系统

文章目录 前言什么是 DNS 域名解析系统为什么需要 DNS 域名解析DNS 是如何发展的hosts 文件维护域名和IP的映射关系DNS 系统&#xff08;服务器&#xff09;DNS 镜像系统 前言 前面为大家分享了关于计算机网络中应用层——自定义协议、传输层——UDP、TCP 协议、网络层——IP协…

Java修仙传之Flink篇

大道三千:最近我修Flink 目前个人理解&#xff1a; 处理有界&#xff0c;无界流的工具 FLINK&#xff1a; FLINK定义&#xff1a; Flink特点 Flink分层API 流的定义 有界数据流&#xff08;批处理&#xff09;&#xff1a; 有界流&#xff1a;数据结束了&#xff0c;程序也…