【Spring】@Transactional事务属性详解
文章目录
- 1、事务传播行为
- 注意事务传播行为在不同类之间调用生效
- Propagation.REQUIRED(默认传播行为)
- Propagation.REQUIRES_NEW
- Propagation.NESTED
- 2、事务的隔离级别
- 隔离级别设置
- 3、设置事务异常回滚
- 3.1、默认情况
- 3.2、设置回滚异常
- 3.3、设置不回滚的异常
- 4、超时时间
- 5、只读
@Transactional
注解开启事务,其中注解的各种属性详解
1、事务传播行为
事务传播行为详解
事务传播行为是为了解决业务层方法之间互相调用的事务问题
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
举个例子:我们在 A 类的aMethod()
方法中调用了 B 类的 bMethod()
方法。这个时候就涉及到业务层方法之间互相调用的事务问题。如果我们的 bMethod()
如果发生异常需要回滚,如何配置事务传播行为才能让 aMethod()
也跟着回滚呢?这个时候就需要事务传播行为的知识了,如果你不知道的话一定要好好看一下
如下 StudentService中的 changeName 方法有运行时异常
@Service
public class TopService {@Autowiredprivate StudentService studentService;//测试 事务的传播行为@Transactionalpublic void topService(){studentService.changeAge();studentService.changeName();}
}@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;//事务的传播行为@Transactionalpublic void changeAge(){studentDao.updateAgeById(998, 1);}//事务的传播行为,@Transactionalpublic void changeName(){studentDao.updateNameById("dasdas", 1);int i = 1/0;}}
在TransactionDefinition
定义中包括了如下几个表示传播行为的常量:
public interface TransactionDefinition {int PROPAGATION_REQUIRED = 0;int PROPAGATION_SUPPORTS = 1;int PROPAGATION_MANDATORY = 2;int PROPAGATION_REQUIRES_NEW = 3;int PROPAGATION_NOT_SUPPORTED = 4;int PROPAGATION_NEVER = 5;int PROPAGATION_NESTED = 6;......
}
不过,为了方便使用,Spring 相应地定义了一个枚举类:Propagation
package org.springframework.transaction.annotation;import org.springframework.transaction.TransactionDefinition;public enum Propagation {REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),NEVER(TransactionDefinition.PROPAGATION_NEVER),NESTED(TransactionDefinition.PROPAGATION_NESTED);private final int value;Propagation(int value) {this.value = value;}public int value() {return this.value;}}
注意事务传播行为在不同类之间调用生效
在同一个类中,对于@Transactional注解的方法调用,事务传播行为不会生效。
这是因为Spring框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。
Propagation.REQUIRED(默认传播行为)
如果父方法有事务,就加入父方法事务,如果没有就新建自己独立的事务!
传播行为在子方法的@Transactional中通过propagation 进行设置。
下面代码中是父方法有事务的情况,propagation 设置为Propagation.REQUIRED,在topService()中调用了studentService.changeAge()和studentService.changeName(),因为事务传播行为为REQUIRED,所以changeAge()和changeName()方法在同一个事务中。
此时changeName()发生运行时异常,两个方法同时回滚, 年龄和名字均不会被修改。
@Service
public class TopService {@Autowiredprivate StudentService studentService;//测试 事务的传播行为@Transactionalpublic void topService(){studentService.changeAge();studentService.changeName();}
}@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;//事务的传播行为@Transactional(propagation = Propagation.REQUIRED)public void changeAge(){studentDao.updateAgeById(998, 1);}//事务的传播行为,@Transactional(propagation = Propagation.REQUIRED)public void changeName(){studentDao.updateNameById("dasdas", 1);int i = 1/0;}}
Propagation.REQUIRES_NEW
不管父方法是否有事务,我都新建事务,都是独立的,子方法都是独立的事务
下面代码中,propagation 设置为Propagation.REQUIRES_NEW,在topService()中调用了studentService.changeAge()和studentService.changeName(),因为事务传播行为为REQUIRES_NEW,所以changeAge()和changeName() 子方法是两个独立的事务
此时changeName()发生运行时异常,changeName()发生回滚,不会影响changeAge()方法,年龄将被修改,名字不会修改。
@Service
public class TopService {@Autowiredprivate StudentService studentService;//测试 事务的传播行为@Transactionalpublic void topService(){studentService.changeAge();studentService.changeName();}
}@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;//事务的传播行为@Transactional(propagation = Propagation.REQUIRES_NEW)public void changeAge(){studentDao.updateAgeById(998, 1);}//事务的传播行为,@Transactional(propagation = Propagation.REQUIRES_NEW)public void changeName(){studentDao.updateNameById("dasdas", 1);int i = 1/0;}}
Propagation.NESTED
如果当前存在事务,就在嵌套事务内执行;
- 在外围方法未开启事务的情况下
Propagation.NESTED
和Propagation.REQUIRED
作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰 - 在外围方法开启事务的情况下
Propagation.NESTED
修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务
举个例子:如果 bMethod()
回滚的话,aMethod()
不会回滚。如果 aMethod()
回滚的话,bMethod()
会回滚。
@Service
Class A {@AutowiredB b;@Transactional(propagation = Propagation.REQUIRED)public void aMethod {//do somethingb.bMethod();}
}@Service
Class B {@Transactional(propagation = Propagation.NESTED)public void bMethod {//do something}
}
2、事务的隔离级别
隔离级别在@Transactional中通过 isolation = Isolation.XXXX 进行设置。
数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:
- 读未提交(READ_UNCOMMITTED):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
- 读已提交(READ_COMMITTED):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
- 可重复读(REPEATABLE_READ):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
- 串行化(SERIALIZABLE):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
关于四种事务隔离级别,和什么是脏读、不可重复读和幻读,具体可查看MYSQL事务隔离级别知识点和文章Innodb中的事务隔离级别和锁的关系
TransactionDefinition
接口中定义了五个表示隔离级别的常量:
public interface TransactionDefinition {......int ISOLATION_DEFAULT = -1;int ISOLATION_READ_UNCOMMITTED = 1;int ISOLATION_READ_COMMITTED = 2;int ISOLATION_REPEATABLE_READ = 4;int ISOLATION_SERIALIZABLE = 8;......
}
和事务传播行为那块一样,为了方便使用,Spring 也相应地定义了一个枚举类:Isolation
public enum Isolation {DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);private final int value;Isolation(int value) {this.value = value;}public int value() {return this.value;}}
隔离级别设置
隔离级别在@Transactional中通过 isolation = Isolation.XXXX 进行设置。
//isolation = 设置事务的隔离级别,mysql默认是repeatable read! @Transactional(isolation = Isolation.REPEATABLE_READ)
public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);studentDao.updateNameById("test1",1);
}
3、设置事务异常回滚
事务异常回滚在@Transactional中通过 rollbackFor = xxxException.class 进行设置。
3.1、默认情况
默认只针对运行时异常回滚,编译时异常不回滚。情景模拟代码如下:
下面程序会终止,且不会回滚,且 updateAgeById(100,1) 的方法 会修改数据库成功
@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/@Transactional(readOnly = false,timeout = 3)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内! new FileInputStream("xxxx");studentDao.updateNameById("test1",1);}
}
3.2、设置回滚异常
rollbackFor属性:指定哪些异常类才会回滚,默认是 RuntimeException and Error 异常方可回滚!
下面程序会终止,但是会回滚,因为 FileNotFoundException 属于 Exception异常,updateAgeById方法修改数据库不成功。
/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/
@Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class)
public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现会回滚new FileInputStream("xxxx");studentDao.updateNameById("test1",1);
}
3.3、设置不回滚的异常
在默认设置和已有设置的基础上,再指定一个异常类型,碰到它不回滚。
noRollbackFor属性:指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!* rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!* noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!*/@Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class,noRollbackFor = FileNotFoundException.class)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(100,1);//主动抛出一个检查异常,测试! 发现不会回滚,因为设置了noRollbackFornew FileInputStream("xxxx");studentDao.updateNameById("test1",1);}
}
4、超时时间
超时时间在@Transactional中通过 timeout = 3 进行设置。
事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。
此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。
概括来说就是一句话:超时回滚,释放资源。
@Service
public class StudentService {@Autowiredprivate StudentDao studentDao;/*** timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!*/@Transactional(readOnly = false,timeout = 3)public void changeInfo(){studentDao.updateAgeById(100,1);//休眠4秒,等待方法超时!try {Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}studentDao.updateNameById("test1",1);}
}
5、只读
对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。
设置方式
// readOnly = true把当前事务设置为只读 默认是false!
@Transactional(readOnly = true)
针对DML动作设置只读模式
会抛出下面异常:
Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
相关文章:
【Spring】@Transactional事务属性详解
文章目录 1、事务传播行为注意事务传播行为在不同类之间调用生效Propagation.REQUIRED(默认传播行为)Propagation.REQUIRES_NEWPropagation.NESTED 2、事务的隔离级别隔离级别设置 3、设置事务异常回滚3.1、默认情况3.2、设置回滚异常3.3、设置不回滚的异常 4、超时时间5、只读…...
通过css3的锚定滚动属性,实现分页加载时让滚动条不闪动
html标签 <div scroll"handleScroll" id"list-container"style"overflow-anchor:auto;overflow-y: auto;height: 80vh"><ul id"talks"v-for"(item,index) in msgList":key"item.roleiditem.timeitem.conten…...
使用Selenium与Scrapy处理动态加载网页内容的解决方法
博客正文(包含详细注释) 引言 在爬虫技术领域,处理动态加载的网页内容常常是一项挑战,尤其是对于那些通过用户滚动或其他交互动态加载更多内容的网站。本文将介绍如何结合使用Selenium和Scrapy来有效处理这类网页。 初探Seleni…...

Linux的权限(二)
目录 前言 文件类型和访问权限(事物属性) 补充知识 文件类型 文件操作权限 修改文件权限 chmod指令 文件权限值的表示方法 字符表示方法 8进制数值表示方法 权限有无带来的影响 修改文件角色 chown与chgrp指令 目录的rwx权限 补充知识 …...

网络服务IP属地发生变化的原因有哪些?
近期,许多用户发现自己的网络服务IP属地发生了变化。原本固定的IP地址不再是静态的,而是发生了变动。这一现象引起了广大用户的关注和疑惑,对网络服务的使用和信息安全产生了影响。为了解决用户的疑虑,我们对此现象进行了深入探究…...
OpenGL 着色器程序的保存和加载(二进制)
背景 为了提高OpenGL 着色器程序的编译和链接速度,我们可以将程序保存为二进制进行加载,可以大幅度提升加载效率。 方法 以下是加载和保存二进制程序的方法。 // 加载着色器程序的二进制文件到已创建的着色器程序中 bool loadPragram(const std::str…...

【Unity 实用工具篇】| 游戏多语言解决方案,官方插件Localization 实现本地化及多种语言切换
前言 【Unity 实用工具篇】| 游戏多语言解决方案,官方插件Localization 实现本地化及多种语言切换一、多语言本地化插件 Localization1.1 介绍1.2 效果展示1.3 使用说明 二、 插件导入并配置2.1 安装 Localization2.2 全局配置 三、多语言映射表3.1 创建多语言文本配…...
疯狂SQL转换系列- SQL for Tencent Cloud VectorDB
为了尽量保证使用者通过统一的SQL标准访问各类型数据库,我们这里开启了“疯狂SQL转换系列”。转换的语法效果不一定是最好的,更多是为用户提供一个统一的数据库交互体验。转换数据库目标的确认更多是内生的。基于我们对业务发展的需要。该向量库SQL转换的…...
Excel中的INDIRECT函数用法
当在 Excel 中使用 INDIRECT 函数时,它可以帮助我们通过引用字符串中的单元格地址来获取对应单元格的值。这个函数非常有用,特别是在需要动态地引用其他单元格的情况下。下面是 INDIRECT 函数的一些用法和示例: 基本用法: INDIREC…...

Spring-temp
IOC/DI实现步骤 1.配置元数据 2.实例化IOC 3.获取Bean 基于XML配置方式 管理组件 1.基于构造函数:有参、无参 2.基于静态工厂方法:有参、无参 依赖注入 1.构造函数 2.setter方法 Bean组件高级特性 1.作用域 2.生命周期 FactoryBean 基于注解 IOC Bean作…...

【C++干货铺】会搜索的二叉树(BSTree)
个人主页点击直达:小白不是程序媛 C系列专栏:C干货铺 代码仓库:Gitee 目录 前言: 二叉搜索树 二叉搜索树概念 二叉搜索树操作 二叉搜索树的查找 二叉搜索树的插入 二叉搜索树元素的删除 二叉搜索树的实现 BSTree结点 …...

【Spring AOP】 动态代理
一.AOP常见的实现方式 1.Spring AOP 2.aspectJ 注意:spring使用的是aspectJ的注解,但实现是spring自身实现的. 二.AOP原理 Spirng AOP原理 , 基于动态代理实现的. 三.代理模式 作用就是提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类…...

NAT——网络地址转换
目录 一、概念 二、NAT的分类 1.静态NAT 1.1 静态NAT的配置 1.2 利用eNSP小实验加强对静态NAT的理解 2、动态NAT 三、NAPT——端口映射 四、Easy IP 使用一个公网地址可以让所有人都上公网 一、概念 随着Internet的发展和网络应用的增多,IPv4地址枯竭已经成为…...
Lambda 表达式的常见用法
文章目录 Lambda 表达式的常见用法使用Lambda表达式集合遍历使用Lambda表达式排序使用Lambda表达式过滤使用Lambda表达式映射使用Lambda表达式归约使用Lambda表达式分组使用Lambda表达式函数式接口的实现使用Lambda表达式线程的创建使用Lambda表达式进行Optional 操作使用Lambd…...
成本管理常用的ChatGPT通用提示词模板
成本分析:如何进行成本分析? 成本核算:如何进行成本核算? 成本控制:如何控制成本? 成本效益分析:如何进行成本效益分析? 成本预测:如何预测成本? 成本决…...
如何在PHP中处理日期和时间?
在 PHP 中,你可以使用内置的 DateTime 类和相关函数来处理日期和时间。以下是一些常见的日期和时间操作的示例: 使用 DateTime 类: 获取当前日期和时间: $currentDateTime new DateTime(); echo $currentDateTime->format(Y-…...

NO-IOT翻频,什么是翻频,电信为什么翻频
1.1 翻频迁移最终的目的就是减少网络的相互干扰,提供使用质量. 1.2 随着与日俱增的网络规模的扩大,网内干扰已成了影响网络的质量标准之一,为了保障电信上网体验,满足用户日益增长的网速需求,更好的服务客户,电信针对…...

云原生之深入解析OOM和CPU节流
一、前言 使用 Kubernetes 时,内存不足 (OOM) 错误和 CPU 节流是云应用程序中资源处理的主要难题,这是为什么呢?云应用程序中的 CPU 和内存要求变得越来越重要,因为它们与云成本直接相关。通过 limits 和 requests ,可…...
数据结构与算法之递归: LeetCode 93. 复原 IP 地址 (Typescript版)
复原 IP 地址 https://leetcode.cn/problems/restore-ip-addresses/ 描述 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。 例如:“0.1.2.201” 和 “192.…...
json模块与jsonpath详解
数据提取之JSON与JsonPATH JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。 JSON和XML的比较可谓不…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...