【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.NESTEDPropagation.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事务隔离级别知识点。

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

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

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

相关文章

【c++】string类的模拟实现

目录 介绍: 一,构造函数和析构函数 二,赋值运算符与流运算符 三,迭代器和运算符重载 四,容器接口函数的实现 1,增删操作 2,查找与插入 3,接口的常规操作 介绍: S…

STM32 map文件详解

文章目录 1. 前言2. 生成 .map 文件3 .map 文件的组成3.1 Section Cross References - 各个源文件之间函数的调用关系3.2 Removing Unused input sections from the image - 移除未使用的模块3.3 Image Symbol Table - 映射符号表:描述各(程序段 / 数据&…

Spark分布式内存计算框架

目录 一、Spark简介 (一)定义 (二)Spark和MapReduce区别 (三)Spark历史 (四)Spark特点 二、Spark生态系统 三、Spark运行架构 (一)基本概念 &#x…

Antd v5.8 modal.confirm 手动控制关闭

开发中经常需要使用操作提示弹窗,在 Antd 中的 Modal 组件提供了 confirm 方法,可以快速实现操作提示弹窗。本文就来介绍一下如何使用 Modal.useModal 创建弹窗,并且实现手动控制弹窗的关闭逻辑。 一、代码演示 import { message, Modal } f…

C++ SEH结构化异常捕获处理(双平台支持 Linux、Windows)。

测试: try_ctor();try_call([](){printf("1111111111111111111111\r\n");int* p NULL;*p 100;throw 1;// try_eeh();}, [](){printf("2222222222222222222222\r\n");}); 设置NULL指针P的值引发程式崩溃,可以被正确捕获&#xff0…

禾匠榜店商城系统 RCE漏洞复现

0x01 产品简介 禾匠榜店商城系统是浙江禾匠信息科技有限公司的一套基于PHP和MySQL的商城系统。 0x02 漏洞概述 禾匠榜店商城系统的api/testOrderSubmit模块下的preview方法存在命令执行漏洞,攻击者可以向服务器写入木马文件,直接获取服务器权限 0x03 漏洞概述 FOFA:bod…

2312llvm,编译X86的clang与llvm

先从这里下载llvm-project. 然后解压后,进入clang目录. 输入命令: cmake -DLLVM_ENABLE_PROJECTSclang -G "Visual Studio 17 2022" -A Win32 -Thostx64 ..\llvm表示,用x64的cl编译X86版本,用-A Win32代表X86版本. 然后生成llvm.sln有2M大. 编译选项技巧: 因为,内存…

带你手把手解读firejail沙盒源码(0.9.72版本) (三) fcopy

文章目录 main.c该模块的各个函数功能详解selinux_relabel_pathcopy_filemkdir_attrcopy_linkproc_pid_to_selffs_copydircheckduplicate_dirduplicate_fileduplicate_linkmain Makefile main.c 文件总结 ├── fcopy │ ├── Makefile │ └── main.cmain.c #include…

【腾讯云HAI域探密】- HAI为NPL保驾护航

近些年,随着机器学习技术的蓬勃发展,以GPU为代表的一系列专用芯片以优越的高性能计算能力和愈发低廉的成本,在机器学习领域得到广泛认可和青睐。GPU等专用芯片以较低的成本提供海量算力,已经成为机器学习和AI人工智能领域的核心利…

用EXCEL计算NTC、BS、电压等AD参数

前言 之前计算NTC的AD值算得很麻烦,因为51内核的单片机不支持除法运算,更别说浮点运算了。 EXCEL自动算出参数就显得很方便了。 有纰漏请指出,转载请说明。 学习交流请发邮件 1280253714qq.com 理论基础 参考这篇文章NTC热敏电阻温度采集…

企业数字化转型进入深海区:生成式AI时代下如何制定数据战略

云计算适用于任何人,任何企业,云计算的分支——人工智能(AI)正发展的迅疾如火,炙手可热。特别是ChatGPT已经挑战各行各业对于AI的认知。 作为全球云计算领域的年度风向标活动,12月12日,亚马逊云科技2023 re:Invent中国…

Web漏洞分析-文件解析及上传(下)

随着互联网的迅速发展,网络安全问题变得日益复杂,而文件解析及上传漏洞成为攻击者们频繁攻击的热点之一。本文将深入研究文件解析及上传漏洞,通过对文件上传、Web容器IIS、命令执行、Nginx文件解析漏洞以及公猫任意文件上传等方面的细致分析&…

基于C/C++的非系统库自定义读写ini配置

INI文件由节、键、值组成。 节 [section] 参数 (键值) namevalue 这里将常用的操作方式封装成了一个dll供外部使用 // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 LIBCFG_EXPORTS // 符号…

有什么简单实用的民宿管理系统软件?民宿如何开拓盈利渠道?

民宿是近些年兴起的一种小型住宿设施,一般就是利用当地的一些民房改造的。民宿的装修风格一般更加具有本土风味,很适合拍照打卡。不过民宿由于规模比较小,所以员工一般也不多,很多民宿的入住登记都要旅客自己弄。所以有很多民宿会…

STC8模板代码

目录 STC8依赖文件库 程序结构 GPIO操作 初始化所有 使用宏配置IO口 使用函数配置IO口 UART操作 头文件 初始化 UART1 UART2 UART3 UART4 接收逻辑 UART1 UART2 UART3 UART4 发送 UART1 UART2 UART3 UART4 配置printf Timer操作 导入依赖 初始化 Time…

Apache DolphinScheduler 社区荣获 “2023 年度优秀开源技术团队“ 奖项

在开源社区日益繁荣的今天,我们非常荣幸地宣布:Apache DolphinScheduler 社区在 OSCHINA 平台的评选中荣获了“2023 年度优秀开源技术团队”奖项。这一奖项反映了我们社区在过去一年里在内容发表的深度与广度、活动运营影响力以及对开源文化的推广方面所…

AGILE-SCRUM

一个复杂的汽车ECU开发。当时开发队伍遍布全球7个国家,10多个地区,需要同时为多款车型定制不同的软件,头疼的地方是: 涉及到多方人员协调,多模块集成和管理不同软件团队使用的设计工具、验证工具,数据、工…

JS-sessionStorage、localStorage和cookie

sessionStorage 仅在浏览器当前窗口关闭之前有效,即使是刷新或者进入该窗口下的的另一个页面,数据也仍然存在;关闭浏览器或到另一个窗口,数据就是不存在的。 // 设置sessionStorage保存到本地,第一个为变量名,第二个是…

实验一 门电路逻辑功能及测试

一、实验目的 1.熟悉门电路逻辑功能. 2.熟悉数字电路学习机及示波器使用方法。 二、实验仪器及材料 1.双踪示波器 2.器件 74LS00 二输入端四与非门 2片 74LS20 四输人端双与非门 1片 74LS86 二输入端四异或门 1片 74LS04 六反相器 1片 …

如何在本地搭建Oracle数据库并实现无公网ip通过PLSQL工具远程连接数据库

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 正文开始前给大家推荐个网站,前些天发…