Spring事务详解

news2024/10/6 1:43:54

 

🏆今日学习目标:

🍀Spring事务详解
创作者:林在闪闪发光
⏰预计时间:30分钟
🎉个人主页:林在闪闪发光的个人主页

 🍁林在闪闪发光的个人社区,欢迎你的加入: 林在闪闪发光的社区

目录

事务简介

Spring的事务支持

编程式事务

声明式事务

事务管理模型

事务传播行为

PROPAGATION_REQUIRED

PROPAGATION_REQUIRES_NEW

PROPAGATION_NESTED

PROPAGATION_SUPPORTS

PROPAGATION_NOT_SUPPORTED

PROPAGATION_MANDATORY

PROPAGATION_NEVER

事务隔离级别

事务的超时时间

事务的只读属性

事务的回滚策略

事务的不回滚策略

@Transaction失效场景


射箭如果没有箭靶子那也就没有了拉弓的意义 

事务简介

事务在逻辑上是一组操作,要么执行,要不都不执行。主要是针对数据库而言的,比如说 MySQL。
为了保证事务是正确可靠的,在数据库进行写入或者更新操作时,就必须得表现出 ACID 的 4 个重要特性:

  • 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
  • 事务隔离(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

其中,事务隔离又分为 4 种不同的级别,包括:

  • 未提交读(Read uncommitted),最低的隔离级别,允许“脏读”(dirty reads),事务可以看到其他事务“尚未提交”的修改。如果另一个事务回滚,那么当前事务读到的数据就是脏数据。
  • 提交读(read committed),一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
  • 可重复读(repeatable read),一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
  • 串行化(Serializable),最严格的隔离级别,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用 Serializable 隔离级别。

需要格外注意的是:事务能否生效,取决于数据库引擎是否支持事务,MySQL 的 InnoDB 引擎是支持事务的,但 MyISAM 就不支持。

 

Spring的事务支持

Spring 支持两种事务方式,分别是编程式事务和声明式事务,后者最常见,通常情况下只需要一个 **@Transactional **就搞定了(代码侵入性降到了最低),就像这样:

/**
 * 模拟转账
 */
@Transactional
public void handle() {
 // 转账
 transfer(double money);
 // 减自己的钱
  Reduce(double money);
}

编程式事务

编程式事务是指将事务管理代码嵌入嵌入到业务代码中,来控制事务的提交和回滚。
你比如说,使用 TransactionTemplate 来管理事务:

@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

                try {

                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
}

再比如说,使用 TransactionManager 来管理事务:

@Autowired
private PlatformTransactionManager transactionManager;

public void testTransaction() {

  TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
          try {
               // ....  业务代码
              transactionManager.commit(status);
          } catch (Exception e) {
              transactionManager.rollback(status);
          }
}

就编程式事务管理而言,Spring 更推荐使用 TransactionTemplate。
在编程式事务中,必须在每个业务操作中包含额外的事务管理代码,就导致代码看起来非常的臃肿,但对理解 Spring 的事务管理模型非常有帮助。

声明式事务

声明式事务将事务管理代码从业务方法中抽离了出来,以声明式的方式来实现事务管理,对于开发者来说,声明式事务显然比编程式事务更易用、更好用。
当然了,要想实现事务管理和业务代码的抽离,就必须得用到 Spring 当中的AOP,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

声明式事务虽然优于编程式事务,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的。

事务管理模型

Spring 将事务管理的核心抽象为一个事务管理器(TransactionManager),它的源码只有一个简单的接口定义,属于一个标记接口:

public interface TransactionManager {

}

该接口有两个子接口,分别是编程式事务接口 ReactiveTransactionManager 和声明式事务接口 PlatformTransactionManager。我们来重点说说 PlatformTransactionManager,该接口定义了 3 个接口方法:

interface PlatformTransactionManager extends TransactionManager{
    // 根据事务定义获取事务状态
    TransactionStatus getTransaction(TransactionDefinition definition)
            throws TransactionException;

    // 提交事务
    void commit(TransactionStatus status) throws TransactionException;

    // 事务回滚
    void rollback(TransactionStatus status) throws TransactionException;
}

通过 PlatformTransactionManager 这个接口,Spring 为各个平台如 JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
参数 TransactionDefinition 和 @Transactional 注解是对应的,比如说 @Transactional 注解中定义的事务传播行为、隔离级别、事务超时时间、事务是否只读等属性,在 TransactionDefinition 都可以找得到。
返回类型 TransactionStatus 主要用来存储当前事务的一些状态和数据,比如说事务资源(connection)、回滚状态等。
TransactionDefinition如下:

public interface TransactionDefinition {

 // 事务的传播行为
 default int getPropagationBehavior() {
  return PROPAGATION_REQUIRED;
 }

 // 事务的隔离级别
 default int getIsolationLevel() {
  return ISOLATION_DEFAULT;
 }

  // 事务超时时间
  default int getTimeout() {
  return TIMEOUT_DEFAULT;
 }

  // 事务是否只读
  default boolean isReadOnly() {
  return false;
 }
}

 

Transactional注解如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

 Propagation propagation() default Propagation.REQUIRED;
 Isolation isolation() default Isolation.DEFAULT;
  int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
  boolean readOnly() default false;

}

 

@Transactional 注解中的 propagation 对应 TransactionDefinition 中的 getPropagationBehavior,默认值为 Propagation.REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED)。
@Transactional 注解中的 isolation 对应 TransactionDefinition 中的 getIsolationLevel,默认值为 DEFAULT(TransactionDefinition.ISOLATION_DEFAULT)。
@Transactional 注解中的 timeout 对应 TransactionDefinition 中的 getTimeout,默认值为TransactionDefinition.TIMEOUT_DEFAULT。
@Transactional 注解中的 readOnly 对应 TransactionDefinition 中的 isReadOnly,默认值为 false。

说到这,我们来详细地说明一下 Spring 事务的传播行为、事务的隔离级别、事务的超时时间、事务的只读属性,以及事务的回滚规则。

事务传播行为

当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播,例如,方法可能继续在当前事务中执行,也可以开启一个新的事务,在自己的事务中执行。
声明式事务的传播行为可以通过 @Transactional 注解中的 propagation 属性来定义,比如说:

@Transactional(propagation = Propagation.REQUIRED)
public void savePosts(PostsParam postsParam) {
}

TransactionDefinition 一共定义了 7 种事务传播行为,其中PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW 两种传播行为是比较常用的。

PROPAGATION_REQUIRED

这也是 @Transactional 默认的事务传播行为,指的是如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。更确切地意思是:
如果外部方法没有开启事务的话,Propagation.REQUIRED 修饰的内部方法会开启自己的事务,且开启的事务相互独立,互不干扰。
如果外部方法开启事务并且是 Propagation.REQUIRED 的话,所有 Propagation.REQUIRED 修饰的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务都需要回滚。

也就是说如果a方法和b方法都添加了注解,在默认传播模式下,a方法内部调用b方法,会把两个方法的事务合并为一个事务。

PROPAGATION_REQUIRES_NEW

创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法都会开启自己的事务,且开启的事务与外部的事务相互独立,互不干扰。

当类A中的 a 方法用默认 Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 ,总结就是a不影响b,b影响a

PROPAGATION_NESTED

如果当前存在事务,就在当前事务内执行;否则,就执行与 PROPAGATION_REQUIRED 类似的操作。

当类A中的 a 方法用默认 Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.NESTED模式,然后在 在a 方法里调用 b方法操作数据库,然而 b方法抛出异常后,a方法是不的回滚 ,总结就是b不影响a,a影响b。

PROPAGATION_SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

PROPAGATION_NOT_SUPPORTED

以非事务方式运行,如果当前存在事务,则把当前事务挂起。

PROPAGATION_MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

PROPAGATION_NEVER

以非事务方式运行,如果当前存在事务,则抛出异常。

事务隔离级别

前面我们已经了解了数据库的事务隔离级别,再来理解 Spring 的事务隔离级别就容易多了。
TransactionDefinition 中一共定义了 5 种事务隔离级别:

  • ISOLATION_DEFAULT,使用数据库默认的隔离级别,MySql 默认采用的是 REPEATABLE_READ,也就是可重复读。
  • ISOLATION_READ_UNCOMMITTED,最低的隔离级别,可能会出现脏读、幻读或者不可重复读。
  • ISOLATION_READ_COMMITTED,允许读取并发事务提交的数据,可以防止脏读,但幻读和不可重复读仍然有可能发生。
  • ISOLATION_REPEATABLE_READ,对同一字段的多次读取结果都是一致的,除非数据是被自身事务所修改的,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • ISOLATION_SERIALIZABLE,最高的隔离级别,虽然可以阻止脏读、幻读和不可重复读,但会严重影响程序性能。

通常情况下,我们采用默认的隔离级别 ISOLATION_DEFAULT 就可以了,也就是交给数据库来决定。

事务的超时时间

事务超时**timeout **,也就是指一个事务所允许执行的最长时间,如果在超时时间内还没有完成的话,就自动回滚。
假如事务的执行时间格外的长,由于事务涉及到对数据库的锁定,就会导致长时间运行的事务占用数据库资源。

事务的只读属性

事务的只读属性readOnly, 如果一个事务只是对数据库执行读操作,那么该数据库就可以利用事务的只读属性,采取优化措施,适用于多条数据库查询操作中。
为什么一个查询操作还要启用事务支持呢?
这是因为 MySql(innodb)默认对每一个连接都启用了 autocommit 模式,在该模式下,每一个发送到 MySql 服务器的 SQL 语句都会在一个单独的事务中进行处理,执行结束后会自动提交事务。
那如果我们给方法加上了 @Transactional 注解,那这个方法中所有的 SQL 都会放在一个事务里。否则,每条 SQL 都会单独开启一个事务,中间被其他事务修改了数据,都会实时读取到。
有些情况下,当一次执行多条查询语句时,需要保证数据一致性时,就需要启用事务支持。否则上一条 SQL 查询后,被其他用户改变了数据,那么下一个 SQL 查询可能就会出现不一致的状态。

事务的回滚策略

**回滚策略rollbackFor **,用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。默认情况下,事务只在出现运行时异常(Runtime Exception)时回滚,以及 Error,出现检查异常(checked exception,需要主动捕获处理或者向上抛出)时不回滚。

如果你想要回滚特定的异常类型的话,可以这样设置:

@Transactional(rollbackFor= MyException.class)

事务的不回滚策略

**不回滚策略noRollbackFor **,用于指定不触发事务回滚的异常类型,可以指定多个异常类型。

@Transaction失效场景

  • 作用于非public方法上,之所以会失效是因为在Spring AOP 代理时,如下图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute

方法,获取Transactional 注解的事务配置信息。
此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

 

注意:protected、private修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。

  • propagation设置问题,会导致事务不生效,也就事务不会回滚
  • rollbackFor指定事务回滚的异常类型
  • 同个类中的调用被@transaction修饰的方法,会失效,因为只有当事务方法被当前类以外的代码调用,才会由spring生成的代理对象来管理。
  • try catch导致失效

 

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

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

相关文章

贯穿设计模式第三话--依赖倒转原则

🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳 从今天开始,将…

【GPT4】GPT4 官方报告解读

欢迎关注【youcans的AGI学习笔记】原创作品 【GPT4】GPT-4 官方报告解读1. GPT-4 官方介绍2. GPT-4 的性能2.1 GPT-4 在各种学术和专业考试中的性能2.2 GPT-4 在传统机器学习测试中的性能2.3 GPT-4 在不同语言测试中的性能3. GPT-4 的图像输入功能3.1 GPT-4 图像输入案例3.2 GP…

《分解因数》:质因数分解

目录 一、题目&#xff1a; 二、思路&#xff1a; 三、代码&#xff1a; 一、题目&#xff1a; 分解因数 《分解因数》题目链接 所谓因子分解&#xff0c;就是把给定的正整数a&#xff0c;分解成若干个素数的乘积&#xff0c;即 a a1 a2 a3 ... an,并且 1 < a1…

<数据结构> 链表 - 单链表(c语言实现)

B.最简单结构的链表——不带哨兵位单链表的实现&#xff08;关于哨兵位结点&#xff09; 一、不带哨兵位单链表结点的创建1.1 typedef 链表的数据类型 1.2 结点的结构体创建 二、单链表要实现的功能 三、需要包含的头文件四、函数接口一览为什么有些函数参数传递的是二级指针&a…

【FreeRTOS(二)】FreeRTOS新手入门——计数型信号量和二进制信号量的基本使用并附代码解析

写在前面&#xff1a; 本文章如有错漏之处&#xff0c;敬请指正&#xff0c;另外本文为网络材料整理&#xff0c;侵删。 FreeRTOS信号量的基本使用&代码解析一、信号量概述二、计数型信号量三、二进制信号量四、信号量函数API1、创建信号量2、删除一个信号量3、信号量释放4…

ASP.NET动态Web开发技术第5章

第5章数据验证一.预习笔记 1.验证控件概述&#xff1a; 2.RequiredFieldValidator&#xff08;必填验证&#xff09; 常用属性1&#xff1a;ControlToValidator:被验证的输入控件的ID 常用属性2&#xff1a;Text&#xff1a;验证失败时&#xff0c;验证控件显示的文本 常用…

8.3 总体分布的假设检验

学习目标&#xff1a; 如果我要学习总体分布的假设检验&#xff0c;我会采取以下步骤&#xff1a; 掌握基础概念&#xff1a;学习和掌握统计学中基础的概念&#xff0c;如总体、样本、假设检验、p值等等。 学习检验方法&#xff1a;了解和学习不同的总体分布假设检验方法&…

亚信科技AntDB数据库荣膺第十二届数据技术嘉年华(DTC 2023)“最具潜力数据库”大奖

近日&#xff0c;亚信科技AntDB数据库产品在第十二届数据技术嘉年华&#xff08;DTC 2023&#xff09;峰会上斩获“2022年度最具潜力数据库”大奖。亚信安慧副总裁张桦先生受邀参会&#xff0c;并发表了《AntDB数据库通信行业核心系统应用与创新》的主题演讲&#xff0c;分享了…

vue实现好看的相册、图片网站

目录 一、效果图 1.项目访问地址 2.画虫官方效果图&#xff1a; 3.作者实现的效果图&#xff1a; 二、代码实现 1.项目结构截图 2.路由配置代码&#xff1a; 3. 头部底部主页面内容显示容器的代码 4.首页&#xff0c;即标签页的代码 三、项目启动说明 四、总结 一、…

Android---MVC/MVP/MVVM的演进

目录 一个文件打天下 一个文件--->MVC MVC--->MVP MVP--->MVVM 6大设计原则 完整demo 我们通过"#字棋"游戏来展现MVC-->MVP-->MVVM 之间的演进 一个文件打天下 数据、视图以及逻辑都放在一个 class 里面。而一个 class 里最多 500 行代码&…

springboot 密码加密

首先介绍一下jasypt的使用方法 版本对应的坑 使用的时候还是遇到一个坑&#xff0c;就是jasypt的版本与spring boot版本存在对应情况。可以看到jasypt是区分java7和java8的&#xff0c;也存在依赖spring版本的情况。 自己尝试了一下 在使用jasypt-spring-boot-starter的前提…

优思学院|职场达人有什么晋升秘诀?

作为职场人士&#xff0c;升职晋升是我们一直追求的目标。然而&#xff0c;在职场中&#xff0c;竞争是激烈的&#xff0c;只有那些真正做到了突出表现和积极进取的人才能获得晋升机会。这里将分享七个职场达人的晋升秘诀&#xff0c;希望对那些正在寻找升职机会的人有所帮助。…

Python圈的普罗米修斯——一套近乎完善的监控系统

文章目录前言一、怎么采集监控数据&#xff1f;二、采集的数据结构与指标类型2.1 数据结构2.2 指标类型2.3 实例概念2.4.数据可视化2.5.应用前景总结前言 普罗米修斯(Prometheus)是一个SoundCloud公司开源的监控系统。当年&#xff0c;由于SoundCloud公司生产了太多的服务&…

SQL综合查询下

SQL综合查询下 目录SQL综合查询下18、查询所有人都选修了的课程号与课程名题目代码题解19、SQL查询&#xff1a;查询没有参加选课的学生。题目代码20、SQL查询&#xff1a;统计各门课程选修人数&#xff0c;要求输出课程代号&#xff0c;课程名&#xff0c;有成绩人数&#xff…

Express使用

文章目录Express 使用概述下载Express简单使用Express 生成器安装生成器使用基本路由使用路由获取请求数据获取路由参数处理请求体设置响应方式一&#xff1a;兼容http模块方式二&#xff1a;express的响应方法其他响应中间件简介全局中间件路由中间件静态资源中间件Router简介…

SkyWalking服务应用

文章目录SkyWalking服务应用案例准备案例实施1.部署Elasticsearch服务2.部署SkyWalking OAP服务3.部署SkyWalking UI服务4.搭建并启动应用商城服务SkyWalking服务应用 案例准备 节点规划 IP主机名节点192.168.100.10node-1Skywalking实验节点192.168.100.20mall商城搭建节点…

【毕业设计】基于程序化生成和音频检测的生态仿真与3D内容生成系统----程序化生成地形算法设计

2 程序化生成地形算法设计 2.1 地形曲线的生成 2.1.1 初始化高度场 struct Make2DGridPrimitive : INode {virtual void apply() override {size_t nx get_input<NumericObject>("nx")->get<int>();nx std::max(nx, (size_t)1);size_t ny has_in…

适配器详解

目录 1、适配器简介 2、函数对象适配器 ​编辑 3、函数指针作为适配器 ptr_fun ​编辑 4、类中的成员函数作为适配器 mem_fun_ref 5、取反适配器 5.1、not1 一元取反适配器 ​编辑 5.2、not2 二元取反适配器 1、适配器简介 适配器 为算法 提供接口目前的适配器最多能扩…

第一次习题总结

目录 求第K个数 求逆序对的数量 数的三次方根 一维前缀和 二维前缀和&#xff08;子矩阵的和&#xff09; 求第K个数 思路&#xff1a;用快速选择&#xff0c;时间复杂度为O(N) sl和sr是左边和右边数的个数&#xff0c;当k<sl&#xff0c;即倒数第K个数在左边范围内&#x…

【JY】减隔震设计思考:隔震篇

【写在前文】随着隔标颁布&#xff0c;国内外大大小小的地震的经历。越来越多的人重视减隔震分析和设计&#xff0c;也听到不少的疑惑声音&#xff0c;个人也有一点热点问题的感悟与大家分享。在个人看来&#xff1a;建筑减隔震&#xff1a;七分构造三分算&#xff01;特别注意…