Spring事务失效场景

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

一、数据库引擎不支持事务

以 MySQL为例,MyISAM引擎是不支持事务操作的,一般要支持事务都会使用InnoDB引擎
从MySQL 5.5.5 开始的默认存储引擎是 InnoDB,之前默认的都是 MyISAM,所以这一点要值得注意,如果底层引擎不支持事务,那么再怎么设置也没有用。
在这里插入图片描述

二、没有被 Spring 管理

此时把@Service注解注释掉,那么这个类就不会被加载成一个Bean,这个类就不会Spring管理了,事务自然就失效了。

public class OrderServiceImpl implements OrderService{@Transactionalpublic void updateOrder(Order order){//update order}
}

因为Spring事务是由AOP机制实现的,也就是说从Spring IOC容器获取bean时,Spring会为目标类创建代理,来支持事务的。但是@Service被注释后,service类都不是spring管理的

三、方法不是 public 的或者被final 、static关键字修饰

@Transactional注解只能用于public 的方法上,否则事多不会生效,如果要用在非public的方法上,则可以开启基于 AspcetJ 框架的静态代理模式。
Spring事务是由AOP机制实现的,AOP机制的本质就是动态代理,而代理的事务方法不是public的话,computeTransactionAttribute()就会返回null,也就是这时事务属性不存在了。

@Service
public class TianLuoServiceImpl implements TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;//spring事务方法addTianLuo的访问权限不是public@Transactionalprivate void addTianLuo(TianLuo tianluo) {tianLuoMapper.save(tianluo);tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));}
}

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

@Service
public class MyServiceImpl{@Autowiredprivate MyMapper myMapper;@Autowiredprivate MyFlowMapper myFlowMapper;//如果一个方法被声明为final或者static,则该方法不能被子类重写,//也就是说无法在该方法上进行动态代理,这会导致Spring无法生成事务代理对象来管理事务。@Transactionalpublic final void addSomething(Things things){myMapper.save(things);myFlowMapper.saveFlow(buildFlowByMy(things));}
}

四、发生自身调用

事务是通过Spring AOP代理来实现的,而在同一个类中,一个方法调用另一个方法时,调用方法直接调用目标方法的代码,而不是通过代理类进行调用。

@Service
public class TianLuoServiceImpl implements TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;public void addTianLuo(TianLuo tianluo){// 调用内部的事务方法//调用目标executeAddTianLuo方法不是通过代理类进行的,因此事务不生效this.executeAddTianLuo(tianluo);}@Transactionalpublic void executeAddTianLuo(TianLuo tianluo) {tianLuoMapper.save(tianluo);tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));}
}

解决方案:可以新建多一个类,让这两个方法分开,分别在不同的类中。如下:

@Service
public class TianLuoExecuteServiceImpl implements TianLuoExecuteService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;@Transactionalpublic void executeAddTianLuo(TianLuo tianluo) {tianLuoMapper.save(tianluo);tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));}
}@Service
public class TianLuoAddServiceImpl implements TianLuoAddService {@Autowiredprivate TianLuoExecuteService tianLuoExecuteService;public void addTianLuo(User user){tianLuoExecuteService.executeAddTianLuo(user);}
}

有时候也可以在该 Service 类中注入自己,或者通过AopContext.currentProxy()获取代理对象。

五、没有配置事务管理器

如果没有配置以下DataSourceTransactionManager数据源事务管理器,那么事务也不会生效 :

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);
}  

但在 Spring Boot 中只要引入了 spring-boot-starter-data-jdbc 启动器依赖就会自动配置DataSourceTransactionManager数据源事务管理器,所以 Spring Boot框架不存在这个问题,但在传统的 Spring 框架中需要注意。

解决方案:(如果是Spring Boot项目,它默认会自动配置事务管理器并开启事务支持。)

@Configuration
public class AppConfig {@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}
}@Service
public class MyService {@Transactionalpublic void doSomething() {// ...}
}

六、设置了不支持事务

@Service
public class OrderServiceImpl implements OrderService {@Transactionalpublic void update(Order order) {updateOrder(order);}@Transactional(propagation = Propagation.NOT_SUPPORTED)public void updateOrder(Order order) {//update order}
}

这里的Propagation.NOT_SUPPORTED表示当前方法不以事务方式运行,当前若存在事务则挂起,这就是主动不支持以事务方式运行了

七、异常没有被抛出

这个方法把异常给捕获了,但没有抛出来,所以事务不会回滚,只有捕捉到异常事务才会生效。

@Service
public class TianLuoServiceImpl implements TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;@Transactionalpublic void addTianLuo(TianLuo tianluo) {try {//保存tianluo数据库记录tianLuoMapper.save(tianluo);//保存tianluo flow数据库记录tianLuoFlowMapper.saveFlow(tianluo);} catch (Exception e) {log.error("add TianLuo error,id:{},message:{}", tianluo.getId(),e.getMessage());}}}

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

@Service
public class TianLuoServiceImpl implements TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;@Transactional(rollbackFor = Exception.class)public void addTianLuo(TianLuo tianluo) {try {tianLuoMapper.save(tianluo);tianLuoFlowMapper.saveFlow(tianluo);} catch (Exception e) {log.error("add TianLuo error,id:{},message:{}", tianluo.getId(),e.getMessage());throw e;}}
}

手动抛了别的异常
Spring默认只处理RuntimeException和Error,对于普通的Exception不会回滚,除非,用rollbackFor属性指定配置
解决方案:添加属性配置@Transactional(rollbackFor = Exception.class)

@Service
public class TianLuoServiceImpl implements TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;@Transactionalpublic void addTianLuo(TianLuo tianluo) throws Exception {tianLuoMapper.save(tianluo);tianLuoFlowMapper.saveFlow(tianluo);throw new Exception(); //手动抛了Exception异常,但是是不会回滚的}
}

八、异常类型不匹配

其实rollbackFor属性指定的异常必须是Throwable或者其子类。默认情况下,RuntimeException和Error两种异常都是会自动回滚的。但是因为以上的代码例子,指定了rollbackFor = Error.class,但是抛出的异常又是Exception,而Exception和Error没有任何什么继承关系,因此事务就不生效。

@Service
public class TianLuoServiceImpl implements TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;//其实rollbackFor属性指定的异常@Transactional(rollbackFor = Error.class)public void addTianLuo(TianLuo tianluo) {tianLuoMapper.save(tianluo);tianLuoFlowMapper.saveFlow(tianluo);//模拟异常抛出throw new Exception();}
}

在这里插入图片描述

九、配置错误的 @Transactional 注解

虽然使用了@Transactional注解,但是注解中的readOnly=true属性指示这是一个只读事务,因此在更新User实体时会抛出异常。

@Transactional(readOnly = true)
public void updateUser(User user){userDao.updateUser(user);
}

解决方案:将readOnly属性设置为false,或者移除了@Transactional注解中的readOnly属性。

十、事务超时时间设置过短

timeout属性被设置为1秒,这意味着如果事务在1 秒内无法完成,则报事务超时了。

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

十一、使用了错误的事务传播机制

@Service
public class TianLouServiceImpl{@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;//Propagation.NOT_SUPPORTED传播特性不支持事务@Transactional(propagation = Propagation.NOT_SUPPPRTED)public void doInsertIianluo(TianLuo tianluo)throws Exception{tianLuoMapper.save(tianluo);tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));}
}

选择正确的事务传播机制:Spring提供了七种事务传播机制

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

十二、事务注解被覆盖导致事务失效

这将导致数据不一致的问题,因为在MyRepository的save()方法中进行的数据库操作将不会回滚。

public interface MyRepository {@Transactionalvoid save(String data);
}public class MyRepositoryImpl implements MyRepository {@Overridepublic void save(String data) {// 数据库操作}
}public class MyService {@Autowiredprivate MyRepository myRepository;//由于子类方法中的注解覆盖了父类的注解,Spring框架将不会在父类的方法中启动事务。@Transactionalpublic void doSomething(String data) {myRepository.save(data);}
}public class MyTianluoService extends MyService {//传播行为(REQUIRES_NEW),子类方法中的注解覆盖了父类的注解@Transactional(propagation = Propagation.REQUIRES_NEW)public void doSomething(String data) {super.doSomething(data);}
}

十三、嵌套事务

@Service
public class TianLuoServiceInOutService {@Autowiredprivate TianLuoFlowService tianLuoFlowService;@Autowiredprivate TianLuoMapper tianLuoMapper;//导致tianLuoMapper.save也会回滚啦。@Transactionalpublic void addTianLuo(TianLuo tianluo) throws Exception {tianLuoMapper.save(tianluo);tianLuoFlowService.saveFlow(tianluo);}
}@Service
public class TianLuoFlowService {@Autowiredprivate TianLuoFlowMapper tianLuoFlowMapper;//如果saveFlow出现运行时异常,会继续往上抛,到外层addTianLuo的方法@Transactional(propagation = Propagation.NESTED)public void saveFlow(TianLuo tianLuo) {tianLuoFlowMapper.save(tianLuo);throw new RuntimeException();}
}

解决方案: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());}}

十四、事务多线程调用

Spring事务是基于线程绑定的,每个线程都有自己的事务上下文,而多线程环境下可能会存在多个线程共享同一个事务上下文的情况,导致事务不生效。
Spring事务管理器通过使用线程本地变量(ThreadLocal)来实现线程安全。

@Service
public class TianLuoService {@Autowiredprivate TianLuoMapper tianLuoMapper;@Autowiredprivate TianLuoFlowService tianLuoFlowService;@Transactionalpublic void addTianLuo(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事务管理器中,通过TransactionSynchronizationManager类来管理事务上下文。TransactionSynchronizationManager内部维护了一个ThreadLocal对象,用来存储当前线程的事务上下文。在事务开始时,TransactionSynchronizationManager会将事务上下文绑定到当前线程的ThreadLocal对象中,当事务结束时,TransactionSynchronizationManager会将事务上下文从ThreadLocal对象中移除。

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

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

相关文章

1.openEuler概述及安装指南(一)

openEuler OECA认证辅导,标红的文字为学习重点和考点。 1.openEuler简介 openEuler是一款面向全球的开源操作系统 支持ARM、X86等多种处理器,能够充分释放计算芯片的潜能:高效、稳定、安全 适用于数据库、大数据、云计算、人工智能等多种应用场景 通过社区合作,打造创新平…

[面试] 什么是死锁? 如何解决死锁?

什么是死锁 死锁,简单来说就是两个或者多个的线程在执行的过程中,争夺同一个共享资源造成的相互等待的现象。如果没有外部干预线程会一直阻塞下去. 导致死锁的原因 互斥条件,共享资源 X 和 Y 只能被一个线程占用; 请求和保持条件&#xf…

Chapter 8 - 18. Congestion Management in TCP Storage Networks

Comparison with Lossless Networks Recall that in Fibre Channel fabrics (explained in Chapter 3, “Detecting Congestion in Fibre Channel Fabrics”) and lossless Ethernet networks (explained in Chapter 7), when the source of congestion is within an end-devi…

U版YOLO-World来了,YOLOv8再度升级,三行代码上手YOLO-World!

本文首发:AIWalker 欢迎关注AIWalker,近距离接触底层视觉与基础AI https://arxiv.org/abs/2401.17270 https://github.com/AILab-CVC/YOLO-World https://github.com/ultralytics/ultralytics https://www.yoloworld.cc/ YOLO-World亮点 YOLO-World是下…

安珀警报发挥重要作用

在周二(2月13日)的傍晚时分,美国加州发生了一起令人震惊的绑架事件。一名年仅四岁的男童在其父送外卖时被一名嫌犯带走。在加州公路巡逻队和长滩警察局发布安珀警报后,全城的人们都行动了起来。 安珀警报,全名为Ameri…

MiKTeX安装后,Latex编译后PDF无法预览,是灰色的

解决方式删掉编译器就可以, 即删掉MiKTeX MiKTeX安装后会将编译器默认修改为MiKTeX,这个时候会显示报错,简单粗暴的方式是删掉MiKTeX软件

AI:135-基于卷积神经网络的艺术品瑕疵检测与修复

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…

YOLOv9:目标检测的新里程碑

YOLOv9:目标检测的新里程碑 摘要:YOLOv9是目标检测领域的一个重大突破,它在性能、速度和准确性方面都取得了显著的进步。本文详细介绍了YOLOv9的架构创新、优化策略以及在实际应用中的表现,并通过与YOLOv8等先前版本的比较&#…

Github 2024-02-21 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-02-21统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目8非开发语言项目1TypeScript项目1 gpt4free 语言模型集合改进计划 创建周期:300 天开…

并发编程(4)共享模型之无锁

6 共享模型之无锁 本章内容 CAS 与 volatile原子整数原子引用原子累加器Unsafe 6.1 问题提出 有如下需求,保证 account.withdraw 取款方法的线程安全 import java.util.ArrayList; import java.util.List;interface Account {// 获取余额Integer getBalance();…

复旦大学MBA:AIGC时代,科技与商业迸发更绚烂的火花

ChatGPT问世以来,AI技术及应用进入一个全速推进的通道,快速迈入通用大模型时代。从AGI(人工通用智能)到AIGC(AI多模态内容生成),AI正在飞速重塑各个行业、人类生活乃至人类的未来。在商业领域更是给营销场景和营销工具…

Mac OS 搭建C++开发环境【已解决】

Mac OS 搭建C开发环境 文章目录 Mac OS 搭建C开发环境一、安装命令行工具:二、安装vscode三、安装gcc3.1 安装Homebrew3.2 安装gcc3.3 修改配置 四、更改VSCode默认编译器五、安装gdb六、安装Cmake && git七、编译运行 本地环境: Mac OS Sonoma …

3.openEuler物理存储及逻辑卷管理(一):磁盘存储挂载与使用

openEuler OECA认证辅导,标红的文字为学习重点和考点。 如果需要做实验,建议下载麒麟信安、银河麒麟、统信等具有图形化的操作系统,其安装与openeuler基本一致。 磁盘大类: HDD, (Hard Disk Drive的缩写) : 由一个或者多个铝制或者玻璃制成的磁性碟 片,磁头,…

xtu oj 1248 Alice and Bob 2.0

题目描述 Alice和Bob在玩骰子游戏,他们用三颗六面的骰子,游戏规则如下: 点数的优先级是1点最大,其次是6,5,4,3,2。三个骰子点数相同,称为"豹子",豹子之间按点数优先级比较大小。如果只有两个骰…

Python截取视频帧

方式一 遍历指定目录下所有的视频,按照指定时间间隔截取视频帧 import os import cv2 import sys import timedef save_frame(time_in_sec, file_path):# 打开视频文件cap cv2.VideoCapture(file_path)# 获取视频的帧率fps cap.get(cv2.CAP_PROP_FPS)# 获取视频…

Kafka入门介绍一

介绍 Kafka是一个分布式系统,由服务器和客户端组成,通过高性能TCP网络协议进行通信。它可以部署在本地和云中的裸机硬件、虚拟机和容器上环境。 服务器:Kafka作为一个或多个服务器的群集运行,这些服务器可以跨越多个数据中心或云…

1007: 【C1】【一维数组】【入门】数组元素的查找

题目描述 给你n个整数&#xff0c;并且给你要查找的整数k。 在n个整数中查找整数k是否存在&#xff0c;如果找到则输出k在n个整数中第一次出现的位置&#xff0c;如果找不到则输出-1 输入 第一行&#xff1a;一个整数n&#xff0c;表示有n个数 (1<n<100) 第二行&am…

React18源码: Fiber树中的全局状态与双缓冲

Fiber树构造 在React运行时中&#xff0c;fiber树构造位于 react-reconciler 包在正式解读 fiber 树构造之前&#xff0c;再次回顾一下renconciler的4个阶段 1.输入阶段&#xff1a;衔接react-dom包&#xff0c;承接fiber更新请求2.注册调度任务&#xff1a;与调度中心(schedu…

数据存储-文件存储

一、CSV文件存储 csv是python的标准库 列表数据写入csv文件 import csvheader [班级, 姓名, 性别, 手机号, QQ] # 二维数组 rows [[学习一班, 大娃, 男, a130111111122, 987456123],[学习二班, 二娃, 女, a130111111123, 987456155],[学习三班, 三娃, 男, a130111111124, …

spring aop应用---记录日志

引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-la…