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://xiahunao.cn/news/2807473.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

CSP-J 2023 复赛第4题:旅游巴士

【题目来源】https://www.luogu.com.cn/problem/P9751https://www.acwing.com/problem/content/description/5313/【题目描述】 小 Z 打算在国庆假期期间搭乘旅游巴士去一处他向往已久的景点旅游。 旅游景点的地图共有 n 处地点,在这些地点之间连有 m 条道路。 其中…

【Linux进程】进程状态---进程僵尸与孤儿

📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1.进程排队2.进程状态…

高考杂志高考杂志社高考编辑部2023年第32期目录

高考论坛 高中数学课堂教学中创设有效情境的策略探究 黄进生; 3-5 核心素养为导向的高中物理教学探究 王丽萍; 6-8 高中化学“教、学、评”一体化教学模式的有效应用 陈燕; 9-11《高考》投稿:cn7kantougao163.com 新高考背景下高中英语阅读理解教学…

手机单目相机内参标定

使用软件: 参考我之前的文章: 软件地址:https://github.com/DavidGillsjo/VideoIMUCapture-Android/releases 棋盘标定板下载 链接: https://pan.baidu.com/s/1wiPJsEf87Vc0D7KwJnt3GA?pwd1234 提取码: 1234 过程 1.使用下载的软件录制一段视频&am…

Ps:直方图

直方图 Histogram是一个用二维坐标表示图像像素或分量值强度分布的图形。 Ps菜单:窗口/直方图 Window/Histogram 几乎所有的图像处理软件里都有直方图,大多数的相机里也内置了直方图。 ◆ ◆ ◆ 直方图的构成 直方图是一个二维坐标系统,横坐…

docker安装flink

docker安装flink 5.1、拉取flink镜像,创建网络 docker pull flink docker network create flink-network5.2、创建 jobmanager # 创建 JobManager docker run \-itd \--namejobmanager \--publish 8081:8081 \--network flink-network \--env FLINK_PROPERTIES&…

橘子学es原理01之准备工作

es本身是具备很好的使用特性的,我指的是他的部署方面的,至于后期的使用和运维那还是很一眼难尽的。 我们从这一篇开始就着重于es的一些原理性的的一些探讨,当然我们也会有一些操作性的,业务性的会分为多个栏目来写。比如前面我写的…

hbuilderx创建、运行uni-app

创建uni-app 在点击工具栏里的文件 -> 新建 -> 项目: 选择uni-app类型,输入工程名,选择模板,点击创建,即可成功创建。 uni-app自带的模板有 Hello uni-app ,是官方的组件和API示例。还有一个重要模…

编码后的字符串lua

-- 长字符串 local long_string "你好你好你好你好你好你好你好你好" local encoded_string "" for i 1, #long_string do local char_code string.byte (long_string, i) encoded_string encoded_string .. char_code .. "," end encoded_…

vulnhub靶场之driftingblues-1

一.环境搭建 1.靶场描述 get flags difficulty: easy about vm: tested and exported from virtualbox. dhcp and nested vtx/amdv enabled. you can contact me by email (it should be on my profile) for troubleshooting or questions. 2.靶场下载 https://www.vulnhub.…

贪婪算法入门指南

想象一下,你在玩一款捡金币的游戏。在这个游戏里,地图中散布着各种大小不一的金币,而你的目标就是尽可能快地收集到最多的金币。你可能会采取一个直观的策略:每次都去捡最近的、看起来最大的金币。这种在每一步都采取局部最优解的…

【Linux基础】Linux自动化构建工具make/makefile

背景 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后…

FPGA之16:1复选器

每个slice 都有一个F8MUX。F8MUX原语: MUXF8 MUXF8_inst( .0(0),Il Output of MUX to general routing .I0(10),//Input(tie to MUXF7L/LO out) .I1&#xf…

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

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

《Docker 简易速速上手小册》第3章 Dockerfile 与镜像构建(2024 最新版)

文章目录 3.1 编写 Dockerfile3.1.1 重点基础知识3.1.2 重点案例:创建简单 Python 应用的 Docker 镜像3.1.3 拓展案例 1:Dockerfile 优化3.1.4 拓展案例 2:多阶段构建 3.2 构建流程深入解析3.2.1 重点基础知识3.2.2 重点案例:构建…

基于Java+SSM+Jsp宿舍管理系统(源码+演示视频+包运行成功+Maven版)

您好,我是码农小波(wei158888),感谢您阅读本文,欢迎一键三连哦。 ❤️ 1. 毕业设计专栏,毕业季咱们不慌,上千款毕业设计等你来选。 目录 1、项目背景 2、项目演示 3、使用技术 4、系统设计 …

Unity中URP实现水体效果(泡沫)

文章目录 前言一、给水上色1、我们在属性面板定义两个颜色2、在常量缓冲区申明这两个颜色3、在片元着色器中,使用深度图对这两个颜色进行线性插值,实现渐变的效果 二、实现泡沫效果1、采样 泡沫使用的噪波纹理2、控制噪波效果强弱3、定义_FoamRange来控制…

Android14之input高级调试技巧(一百八十八)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

Android 内存优化内存泄漏处理

一:匿名内部类/非静态内部类 匿名内部类的泄漏原因:匿名内部类会隐式地持有外部类的引用.当外部类被销毁时,内部类并不会自动销毁,因为内部类并不是外部类的成员变量, 它们只是在外部类的作用域内创建的对象,所以内部…

数据结构与算法相关题解20240225

数据结构与算法相关题解20240225 一、58. 最后一个单词的长度二、48. 旋转图像三、69. x 的平方根四、50. Pow(x, n) 一、58. 最后一个单词的长度 简单 给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度…