spring boot 之 事务

内容是小老弟的一些整理和个人思考总结,知识的海洋那么大,有错误的话还请诸位大佬指点一下!

事务是一个不可分割操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。
事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
在应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。

事务的四个特性

原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。

一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。

隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

事务隔离级别

序号隔离级别描述
1PROPAGATION_REQUIRED(默认值)如果当前存在事务,则在该事务中执行;否则,创建一个新的事务并在其中执行方法。
2PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
3PROPAGATION_MANDATORY强制执行当前事务,如果当前没有事务,则抛出异常。
4PROPAGATION_REQUIRES_NEW创建新的事务,并在新事务中执行方法,如果当前存在事务,则暂停当前事务。
5PROPAGATION_NOT_SUPPORTED以非事务方式执行方法,如果当前存在事务,则暂停当前事务。
6PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
7PROPAGATION_NESTED如果当前存在事务,则在嵌套事务中执行;否则,执行与PROPAGATION_REQUIRED相同的操作。嵌套事务是当前事务的子事务,并与当前事务共享一部分数据源连接。
如果嵌套事务失败,则仅回滚嵌套事务,而不会回滚当前事务。如果当前事务失败,则嵌套事务和当前事务都将被回滚。

事务传播行为

序号传播行为描述
1ISOLATION_DEFAULT默认隔离级别,由底层的数据库驱动决定隔离级别,通常为数据库的默认隔离级别。
2ISOLATION_READ_UNCOMMITTED该隔离级别表示一个事务可以读取另一个事务修改但未提交的数据。该级别可以产生脏读、不可重复读和幻读的问题。
3ISOLATION_READ_COMMITTED该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以避免脏读问题,但不可重复读和幻读问题仍然可能发生。
4ISOLATION_REPEATABLE_READ该隔离级别表示一个事务在执行过程中多次读取同一数据集时,其结果是一致的。该级别可以避免脏读和不可重复读问题,但仍然可能发生幻读问题。
5ISOLATION_SERIALIZABLE该隔离级别表示一个事务在执行过程中对于数据的修改会进行排队,即串行化执行,从而避免了脏读、不可重复读和幻读问题,但也降低了并发性能。

编程式事务

方式一:PlatformTransactionManager

@Autowired
private PlatformTransactionManager platformTransactionManager;@Test
public void test(){DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();defaultTransactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);TransactionStatus transaction = platformTransactionManager.getTransaction(defaultTransactionDefinition);try {// 业务代码// doSomeThing();// 提交事务platformTransactionManager.commit(transaction);} catch (Exception e) {platformTransactionManager.rollback(transaction);throw new ServiceException("特务异常,事务回滚");}
}

方式二:TransactionTemplate

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;import javax.sql.DataSource;
// 配置
@Configuration
public class TransactionConfig {@Autowiredprivate DataSource dataSource;@Beanpublic PlatformTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource);}@Bean("transactionTemplate")public TransactionTemplate transactionTemplate() {DefaultTransactionDefinition def = new DefaultTransactionDefinition();// 配置事务隔离级别def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);// 配置事务传播行为def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);// 以下配置按需配置即可// def.setTimeout(5);  // 设置超时时间为5秒// def.setReadOnly(false);return new TransactionTemplate(transactionManager(), def);}
}
@Autowired
@Qualifier("transactionTemplate")
private TransactionTemplate transactionTemplate;@Test
public void test02() {// new TransactionCallback<Integer> 可更改返回值类型,按需更改即可// execute 业务代码执行后的返回值Integer execute = transactionTemplate.execute(new TransactionCallback<Integer>() {@Overridepublic Integer doInTransaction(TransactionStatus transactionStatus) {int i = 0;try {Student student = new Student();student.setRemark("事务添加测试");i = studentMapper.addOne(student);} catch (Exception e) {transactionStatus.setRollbackOnly();throw new RuntimeException(e);}return i;}});
}

声明式事务

@Transactional常用配置

参数名称功能描述
readOnly用于设置当前事务是否为只读事务,设置为 true 表示只读,false 则表示可读写,默认值为 false。
例如:@Transactional(readOnly=true)
rollbackFor用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
例如:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class);
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
transactionManager / value多个事务管理器托管在 Spring 容器中时,指定事务管理器的 bean 名称
rollbackForClassName用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。
例如:
指定单一异常类名称 @Transactional(rollbackForClassName=”RuntimeException”)
指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
noRollbackFor用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。
例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。
例如:
指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”)
指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”, ”Exception”})
propagation用于设置事务的传播行为。
例如:@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true)
isolation用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
timeout该属性用于设置事务的超时秒数,默认值为 - 1 表示永不超时
事物超时设置:@Transactional(timeout=30) ,设置为 30 秒

@Transactional失效场景

1、权限修饰符问题

@Override
@Transactional
// 被@Transactional注解的方法访问权限必须是public,否则失效,
// 且方法不可用final或者static关键字修饰
public void addOne() {Student student = new Student();student.setRemark("事务添加测试");studentMapper.addOne(student);
}

2、注解属性 rollbackFor设置错误

rollbackFor可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException的异常)或者 Error才回滚事务,其他异常不会触发回滚事务。

// 一般设置为Exception即可,程序中若catch捕获了异常,一定要抛出,否则也会失效,
// 即在 catch 中最后加上 throw new RuntimeExcetpion() 抛出运行异常
@Transactional(rollbackFor = Exception.class)
@Service
@Slf4j
public class MyTransactional {// 最外层公共方法。自动回滚事务方式,insertOrder()方法报错后事务回滚,且线程中止,后续逻辑无法执行@Transactionalpublic void test1() {this.insertOrder();// 若上面代码报错后,下面的代码便不会执行doSomeThingElse();}// 最外层公共方法。手动回滚事务方式,insertOrder()方法报错后事务回滚,可以继续执行后续逻辑@Transactionalpublic void test2() {try {  insertOrder();} catch (Exception e) {  log.error("业务异常,事务回滚", e.getMessage());// 要么手动抛出异常// throw new RuntimeExcetpion();// 要么手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 其他操作doSomeThingElse();}// 进行数据库操作的方法(private 或 public 均可)private void insertOrder() {// 数据库增删改查操作}
}

3、数据库不支持事务

Spring事务生效的前提是连接的数据库支持事务,如果底层的数据库都不支持事务,则Spring事务肯定会失效的。
例如:使用MySQL数据库,默认是InnoDB,若选用MyISAM存储引擎,因为MyISAM存储引擎本身不支持事务,因此事务毫无疑问会失效。

4、未被spring管理

若类没有被spring管理,肯定会失效。

5、多线程调用

两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务,spring的事务是通过数据库连接来实现的。当前线程中保存了一个mapkey是数据源,value是数据库连接。同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务,因为事务失效。

6、未开启事务

Spring项目:需要在applicationContext.xml文件中,手动配置事务相关参数
Spring Boot项目:通过DataSourceTransactionManagerAutoConfiguration类,默认开启了事务

7、方法内部调用

现有一个被外部调用的方法A,A中调用了操作数据库的方法B,说明如下:

方法A未声明事务@Transactional,方法B若是其他类的方法且各自声明事务,则事务由方法B自己控制;

// a和b不在同一个类中场景// a未声明事务,b声明事务,此时a中事务不会回滚,b中的事务可以回滚
// 若想使a中事务也可以回滚,在a上添加事务注解,声明事务即可@Override// @Transactional(rollbackFor = Exception.class)public void aaa() {Student student = new Student();student.setRemark("测试方法 A  " + DateUtil.formatDateTime(new Date()));studentMapper.insert(student);studentServiceV2.bbb();}@Override@Transactional(rollbackFor = Exception.class)public void bbb() {Student student = new Student();student.setRemark("测试方法 B  " + DateUtil.formatDate(new Date()));studentMapper.insert(student);int a = 10 / 0; // 模拟异常}

方法A未声明事务@Transactional,方法B若是本类的方法,则无论方法B是否声明事务,事务均不会生效;

// aaa和a属于同一个类@Overridepublic void aaa() {Student student = new Student();student.setRemark("测试方法 A  " + DateUtil.formatDateTime(new Date()));studentMapper.insert(student);// a方法可以事务回滚的办法// 注意:此时a会回滚事务,但aaa不会回滚事务!!!!!((StudentService)AopContext.currentProxy()).a();}@Override@Transactional(rollbackFor = Exception.class)public void a() {Student student = new Student();student.setRemark("测试方法 a  " + DateUtil.formatDateTime(new Date()));studentMapper.insert(student);int a = 10 / 0; // 模拟异常}

方法A声明事务@Transactional,无论方法B是不是本类的方法,无论方法B是否声明事务,事务均由公共方法A控制;

// aaa和a属于同一个类,b在另一个类中
// 此时只要aaa声明事务,即可实现事务回滚@Override@Transactional(rollbackFor = Exception.class)public void aaa() {Student student = new Student();student.setRemark("测试方法 A  " + DateUtil.formatDateTime(new Date()));studentMapper.insert(student);// studentServiceV2.bbb();a();}@Override// @Transactional(rollbackFor = Exception.class)public void a() {Student student = new Student();student.setRemark("测试方法 a  " + DateUtil.formatDateTime(new Date()));studentMapper.insert(student);int a = 10 / 0; // 模拟异常}@Override// @Transactional(rollbackFor = Exception.class)public void bbb() {Student student = new Student();student.setRemark("测试方法 B  " + DateUtil.formatDate(new Date()));studentMapper.insert(student);int a = 10 / 0; // 模拟异常}

方法A声明事务@Transactional,方法B运行异常,但运行异常被方法B自己 try-catch 处理了,则事务回滚是不会生效的;

异常捕获后一定要抛出!!!

8、错误的传播特性

如果在手动设置propagation参数的时候,把传播特性设置错了,则事务会失效

目前只有这三种传播特性才会创建新事务:REQUIRED,REQUIRES_NEW,NESTED

9、自定义了回滚异常

@Transactional默认回滚RuntimeException运行时异常)和Error错误),但若rollbackFor 指定为自定义的异常,恰巧程序产生的异常不是自定义的异常,事务便不会回滚。

10、大事务问题

该部分其实不属于注解失效场景,算是一个开发问题。

试想:
在被@Transactional注解标记的某个方法中,有5个执行sql语句的操作,其中三个sql是查询操作,只有两个sql大概会产生事务问题,但因为注解标记的是整个方法,所以整个方法都被包含在事务中了,然后又恰巧查询的sql可能略有耗时,那整个事务便会耗时了,从而造成大事务问题。
那咋解决呢?
答案:编程式事务,能够更小粒度的控制事务的范围,更直观,且会尽量的避免声明式事务场景下事务失效问题。
当然,如果你就是非要全部添加事务,那就当我上面是在瞎扯,别杠,杠就是你对。

下述内容整理中,loading…

并发场景下事务引发的问题

脏读

幻读

不可重复读

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

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

相关文章

电商内卷时代,视频号小店凭借一己之力“脱颖而出”

大家好&#xff0c;我是电商笨笨熊 今年618各大电商平台花样百出&#xff1b; 某宝更是直接取消了“预售”&#xff0c;从5月就开始进入618预热期&#xff1b; 不少玩家既开心又难过&#xff0c;市场如此内卷&#xff0c;618确实是个爆发期&#xff0c;但更多的需要不断压低…

Star CCM+分配零部件至区域后交界面丢失-更新找回

前言 在工程应用中&#xff0c;将零部件分配至区域后&#xff0c;一般常规的操作需要对交界面进行检查。偶尔会发现交界面丢失。遇到此类问题&#xff0c;在没有做其他操作前&#xff08;比如画网格&#xff09;&#xff0c;可以选择先删除所有区域在重新分配至区域。若已经进…

基于SSM的大学生兼职管理系统

基于SSM的大学生兼职管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 企业界面 前台学生界面 管理员界面 摘要 随着大学生兼职市场的日益繁…

K8s 高级调度

文章目录 K8s 高级调度CronJobinitContainerTaint 和 Toleration污点&#xff08;Taint&#xff09;容忍&#xff08;Toleration&#xff09; AffinityNodeAffinityPodAnffinity 和 PodAntiAffinity 总结 K8s 高级调度 CronJob 在 k8s 中周期性运行计划任务&#xff0c;与 li…

【vue echart】完成一个简单echart图表+自适应

实现效果&#xff1a; html&#xff1a; <divref"echartOne"id"echartOne"style"width: 100%; height: 100%" ></div> js: getEchartOne() {let chart this.$echarts.init(this.$refs.echartOne);chart.setOption({title: {text:…

linux 有名管道FIFO

无名管道应用的一个重大限制是它没有名字&#xff0c;因此&#xff0c;只能用于具有亲缘关系的进程间通信&#xff0c;在有名管道&#xff08;named pipe或FIFO&#xff09;提出后&#xff0c;该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联&#xff0c;以…

云原生|为什么服务网格能够轻松重塑微服务?一文讲清楚!

目录 一、概述 二、 设计 三、服务网格 四、总结 一、概述 容器化技术与容器编排推动了微服务架构应用的演进&#xff0c;于是应用的扩展与微服务的数量日益增加&#xff0c;新的问题随之而来&#xff0c;监控服务的性能变得越来越困难&#xff0c;微服务与微服务之间相互通…

v-rep--lua接口和c++接口的关联

我们在coppeliasim中调用的lua脚本函数sim.xxxxx()的执行规律有两种情况&#xff1a; 1&#xff0c;要么就是在coppliasim的sim.lua中有这个lua函数的定义&#xff0c;直接执行这个lua函数即可。比如&#xff0c;sim.creatPath(); 2&#xff0c;要么就是这个lua接口没有lua语…

Kafka-集群管理者(Controller)选举机制、任期(epoch)机制

Kafka概述 Kafka-集群管理者&#xff08;Controller&#xff09;选举机制 Kafka中的Controller是Kafka集群中的一个特殊角色&#xff0c;负责对整个集群进行管理和协调。Controller的主要职责包括分区分配、副本管理、Leader选举等。当当前的Controller节点失效或需要进行重新…

嵌入式实时操作系统笔记1:RTOS入门_理解简单的OS系统

今日开始学习嵌入式实时操作系统RTOS&#xff1a;UCOS-III实时操作系统 本次目标是入门RTOS&#xff0c;理解多任务系统...... 本文只是个人学习笔记&#xff0c;基本都是对网上资料的整合...... 目录 STM32裸机与RTOS区别&#xff1a; 裸机中断示例&#xff1a; RTOS对优先级…

汽车标定技术(二十一)--英飞凌TC3xx的OLDA怎么玩?(2)

目录 1.概述 2.Vector提出的OLDA概念 2.1 RAM Copy 2.2 Data Trace 3.小结 1.概述 上一篇汽车标定技术(二十一)--英飞凌TC3xx的OLDA怎么玩?(1)-CSDN博客,我们讲了TC3xx

Spring MVC/Web

1.Spring MVC 的介绍 Spring Web MVC是基于Servlet API构建的原始Web框架&#xff0c;也是Spring框架的一部分。它提供了灵活可扩展的MVC架构&#xff0c;方便开发者构建高性能的Web应用程序&#xff0c;并与 Spring 生态系统无缝集成。 2.MVC 设计模式 MVC&#xff08;Model…

设计模式—23种设计模式重点 表格梳理

设计模式的核心在于提供了相关的问题的解决方案&#xff0c;使得人们可以更加简单方便的复用成功的设计和体系结构。 按照设计模式的目的可以分为三大类。创建型模式与对象的创建有关&#xff1b;结构型模式处理类或对象的组合&#xff1b;行为型模式对类或对象怎样交互和怎样…

CSS实现图片浮动在底层 div 之上,而不会影响底层 div 的布局和内容

前言&#xff1a;遇到个需求&#xff0c;需要图片显示在div之上&#xff0c;但是不占用div的空间布局&#xff0c;网上的答案五花八门&#xff0c;但其实使用css就可以简单实现&#xff0c;仅以此博客作为记录 举个栗子 <div class"container"><img src&qu…

Linux 网络编程基础——网络模型

网络模型 网络模型1. OSI七层模型1. 物理层&#xff08;Physical Layer&#xff09;2. 数据链路层&#xff08;Data Link Layer&#xff09;3. 网络层&#xff08;Network Layer&#xff09;4. 传输层&#xff08;Transport Layer&#xff09;5. 会话层&#xff08;Session Lay…

研发机构大数据迁移如何保障敏感数据不泄露

随着云计算和大数据技术的飞速进步&#xff0c;越来越多的企业正试图通过数据迁移来提升IT基础设施的效率&#xff0c;减少成本&#xff0c;并增强业务的灵活性。但是&#xff0c;这一过程并非没有它的挑战&#xff0c;尤其是在数据安全方面。数据在转移过程中可能会遭遇黑客攻…

SQL的多表联查

这里我先附上两张表的数据&#xff1a; Orders 表: OrderIDCustomerID1321324NULL Customers 表: CustomerIDCustomerName1Alice2Bob3Charlie4David INNER JOIN &#x1f91d; 概念&#xff1a; INNER JOIN&#xff08;内连接&#xff09;返回两个表中匹配的记录。如果某条…

解决深度确定问题:使用不相交集合森林

解决深度确定问题&#xff1a;使用不相交集合森林 引言不相交集合森林&#xff08;DSF&#xff09;基础按秩合并与路径压缩深度确定问题的解决方案实现MAKE-TREE修改FIND-SET实现FIND-DEPTH实现GRAFT分析最坏情况运行时间结论参考文献 引言 在计算机科学中&#xff0c;树结构是…

时空扭曲:重温相对论的终极挑战,探寻真理的脚步

大家都知道&#xff0c;相对论是爱因斯坦提出的划时代理论&#xff0c;为人类认知时空和引力做出了革命性贡献。但这个理论真的万无一失吗&#xff1f;近日&#xff0c;一项新研究提出了测试时间扭曲的新方法&#xff0c;或许能让我们重新审视相对论在宇宙大尺度上的适用性。 时…

HTML5好看的通用网站模板源码

文章目录 1.设计来源1.1 主界面1.2 模板菜单1 界面1.3 模板菜单2 界面1.4 模板菜单3 界面1.5 下拉菜单1 界面1.6 下拉菜单2 界面1.7 模板菜单4 界面1.8 模板菜单5 界面1.9 界面底部 2.效果和源码2.1 动态效果2.2 源码目录2.3 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址…