【Spring内容介绍 | 第一篇】什么是事务管理

news2024/11/20 7:24:47

前言:

        当今软件开发行业中,事务管理是一个不可或缺的重要组成部分。随着企业应用的复杂性和数据交互的增加,确保数据的一致性和完整性变得越来越关键。Spring框架作为一种全功能的应用程序开发框架,为我们提供了强大而灵活的事务管理功能。在这篇文章中,我们将深入探讨Spring框架的事务管理机制,揭示其工作原理和优势。无论您是初学者还是有经验的开发人员,本文将帮助您更好地理解和利用Spring事务管理,以提升应用程序的可靠性和性能。

 

目录

前言:

事务:

事务的基本操作:

Spring中的事务应用场景:

@Transactional:

常见的事务隔离级别:

代码示例:

@Transactional注解属性细节:

1.rollbackFor和noRollbackFor

 2.propagation

 总结:


之前我们在MySQL的文章中就有介绍过什么是事务 :【MySQL数据库 | 第十五篇】事务_数据库提交事务-CSDN博客

简单的来说:事务是一组操作的集合,他是一个不可分割的工作单位,这些操作要么同时成功,妖魔同时失败 

我们举一个简单的例子:银行转账

我们可以简单的认为一共分为这两步操作:
1.给A转账1000元

2.B账户扣除1000元

这两个操作放到数据库中就是两条很简单的语句,但是这两个语句可以分开执行嘛?

答案是不能,因为如果分开执行,先执行1,再执行2,如果B的账户根本没有1000元,那么由于先执行了1,A的账户也会收款1000元,这将是不必要的麻烦

因此我们提出了事务,把1,2操作同时执行,如果2操作不可以成功执行,1操作也不会执行,我们就说我们把1和2操作捆绑成为了一个事务。

事务:

        在Spring框架中,事务是指一组数据库操作(例如插入、更新、删除等)被视为一个不可分割的工作单元,要么全部执行成功,要么全部回滚,以确保数据的一致性和完整性。

Spring的事务管理能力是通过AOP(面向切面编程)代理技术实现的。它提供了一个事务抽象层,使开发人员可以在应用程序中轻松地实现事务功能,而无需过多关注底层事务管理的细节。

Spring中的事务可用于将需要在事务范围内执行的一组数据库操作绑定到一个事务中。通过将这些操作纳入一个事务中,我们可以确保这些操作要么全部成功提交,要么全部回滚,以保持数据的一致性。

Spring框架提供了多种事务管理的策略,包括声明式事务管理和编程式事务管理。声明式事务管理可以通过@Transactional注解XML配置来实现,可以将事务的定义与业务逻辑代码分离。而编程式事务管理则允许开发人员在代码中显式地控制事务的开始、提交和回滚。

通过Spring框架的事务管理特性,开发人员可以更加方便地管理和控制数据库操作的事务性,并确保数据的一致性和完整性。同时,Spring框架在事务管理方面还提供了灵活的配置选项,以适应不同的业务场景和需求。

事务的基本操作:

事务的基本操作通常包括以下几个步骤:

  1. 开启事务(Begin Transaction):事务的第一步是要开启一个事务,这样后续的所有数据库操作都可以在同一个事务中执行。在Spring中,可以使用@Transactional注解或编程式的方式来开启事务。

  2. 执行数据库操作:在事务范围内执行需要进行的数据库操作,包括插入、更新、删除等。这些操作将被纳入事务的管理之下,并在提交或回滚时一起操作。

  3. 提交事务(Commit):当所有的数据库操作成功执行并且不出现异常时,事务可以进行提交。提交时,所有的数据库操作将被正式应用到数据库中,使其生效。在Spring中,事务提交通常是由事务管理器自动完成的。

  4. 回滚事务(Rollback):如果在事务执行期间出现了异常或错误,事务可以选择回滚,即撤销之前的所有数据库操作。这样可以确保数据的一致性和完整性。在Spring中,可以通过捕获异常或进行手动回滚的方式来触发事务的回滚操作。

需要注意的是,事务的范围通常是由方法的边界决定的。即在开启事务前,进入方法执行,执行数据库操作,根据结果决定是提交还是回滚,然后结束事务。整个过程中,事务贯穿始终。

通过以上基本操作,可以实现数据库操作的原子性、一致性、隔离性和持久性(ACID 特性),以确保数据的正确性和可靠性。Spring框架提供了便捷的事务管理机制,使开发人员能够轻松地实现和控制事务的操作。

Spring中的事务应用场景:

如图所示是一个删除指定id部门的操作,而删除了指定id的部门,自然也要删除掉指定id部门的员员工,但是如果在删除完指定id的部门之后,服务器出错抛出了异常,中断了这个操作,那么此时就只删除了部门而没有删除对应部门的员工,这将会在数据库中留下异常的数据,因此我们就需要把这个操作捆绑成为一个事务:删除指定ID的部门和删除对应的员工必须同时执行。 

也就是说对串代码进行这种事务模式的修改:

而正如我们前面所介绍的,由于这些操作都是固定的,因此Spring为我们提供了一个注解@Transactional 

@Transactional:

@Transactional是Spring框架中用于声明式事务管理的注解。通过在方法级别上添加@Transactional注解,可以将方法或类纳入事务管理的范围中,使其具备事务特性。

使用@Transactional注解的好处是,它能够将事务的定义与业务逻辑代码分离,提供了一种声明式的方式来配置和管理事务。开发人员可以通过简单地在方法或类上添加@Transactional注解,而无需编写大量的事务管理代码。

@Transactional注解的常用属性:

  1. propagation:指定事务的传播行为。它定义了在方法调用链中嵌套的情况下,事务是如何传播的。常见的传播行为包括REQUIRED(默认值)、REQUIRES_NEW、NESTED等。

  2. isolation:指定事务的隔离级别。它决定了一个事务对于并发修改的数据可以见到多少。常见的隔离级别包括READ_COMMITTED(默认值)、READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE等。

  3. timeout:指定事务的超时时间。如果事务在指定的超时时间内没有完成,将触发事务的回滚。单位为秒,默认值为-1,表示不设定超时。

  4. readOnly:指定事务是否为只读事务。如果事务只涉及读取数据,并不会进行数据修改操作,可以将readOnly属性设置为true,以优化性能。

  5. rollbackFor和noRollbackFor:用于指定特定的异常类型,当这些异常发生时,事务将回滚或不回滚。

除了以上属性,@Transactional注解还可以在XML配置中进行更细粒度的配置,包括指定基于方法名的事务策略、设置事务管理器、指定事务切面等。

总而言之,@Transactional注解提供了一种方便且简洁的方式来实现声明式事务管理。它使得开发人员可以更加专注于业务逻辑,而无需过多关注底层事务管理的细节。同时,通过灵活的属性配置,可以满足不同场景下的事务需求。

常见的事务隔离级别:

在Spring事务管理中,提供了几种事务隔离级别供选择:

  1. READ_COMMITTED(已提交读):在READ_COMMITTED隔离级别下,事务只能读取到已经提交的数据。它可以避免脏读(Dirty Read),但是可能会发生不可重复读(Non-repeatable Read)和幻读(Phantom Read)。

  2. READ_UNCOMMITTED(未提交读):在READ_UNCOMMITTED隔离级别下,事务可以读取到未提交的数据,可能会发生脏读,也就是读取到未提交的、其它事务正在修改的数据。这个隔离级别下,对数据的一致性要求最低。

  3. REPEATABLE_READ(可重复读):在REPEATABLE_READ隔离级别下,事务在执行期间多次读取相同的数据时,保证能看到一致的数据快照。即使在事务期间有其他事务对数据进行了修改,也不会读到这些修改过的数据。但是,可能会发生幻读问题,即一个事务范围内,两次查询同一个条件返回的结果集不一致。为了避免幻读问题,需要使用锁机制或者更高级别的隔离级别。

  4. SERIALIZABLE(串行化):在SERIALIZABLE隔离级别下,事务会按照串行执行的方式进行,每个事务只能看到其它事务已经提交的数据。它提供了最高的隔离级别,避免了脏读、不可重复读和幻读的问题,但是也会带来最高的并发度降低。

需要根据业务场景和需求选择合适的事务隔离级别。默认情况下,Spring事务将使用数据库的默认隔离级别(通常是READ_COMMITTED),可以通过@Transactional注解的isolation属性来显式指定隔离级别。

值得注意的是,不同的数据库可能对事务隔离级别的支持有所不同,特别是在分布式事务环境中,需要考虑数据库的兼容性和一致性要求。

代码示例:

回到我们之前的代码中:

我们此时手动的给这段代码的执行添加一个异常情况:除零算数异常

我们在前端尝试删除后,可以在控制台看到如下信息:

 这也就意味着我们成功的把这个方法变为了一个事务

@Transactional注解属性细节:

1.rollbackFor和noRollbackFor

在默认情况下,只有出现RuntimeException才回滚异常。而我们前面的rollbackFornoRollbackFor用于指定特定的异常类型,当这些异常发生时,事务将回滚或不回滚。

RuntimeException 是 Java 中的一个类,它是 Exception 类的子类。RuntimeException 是一个非受检异常(Unchecked Exception),在编码和异常处理中具有特殊的地位。

RuntimeException 可以被分为两种类型:

  1. 编程错误引起的异常:这些异常通常是由程序员的错误或代码逻辑错误引起的,例如 NullPointerExeption(空指针异常)、ArrayIndexOutOfBoundsException(数组越界异常)、ClassCastException(类转换异常)等。这些异常发生时通常暗示代码需要进行修复或改进。

  2. 运行环境异常:这些异常通常是由于程序运行环境的问题引起的,例如 ArithmeticException(算术异常,如除零异常)或 OutOfMemoryError(内存溢出错误)。这些异常通常无法通过代码修复,需要进行环境的调整或处理。

RuntimeException 及其子类异常通常不需要在方法声明的 throws 子句中进行声明或捕获处理,也就是说,它们是非受检异常。编译器在编译时不会强制要求处理这些异常,编写代码时可以选择捕获或忽略它们。

值得注意的是,尽管 RuntimeException 是一个非受检异常,但是在编码中应该尽量避免不必要的 RuntimeException 异常的发生,特别是那些可以通过更严谨的代码或输入验证来预防的异常。在实际开发中,合理的异常处理和错误处理将提高代码质量和稳定性。

不属于RuntimeException 异常的异常种类:

  1. Checked Exception(受检异常):这些异常都是 Exception 的子类,但不是 RuntimeException 的子类。受检异常在方法的声明中需要显式地进行捕获或在方法签名的 throws 子句中进行声明。这些异常通常表示外部因素或错误,例如 IOException(输入输出异常)、SQLException(数据库异常)等。

  2. ErrorError 是 Throwable 的子类,与 RuntimeException 并列,它表示 Java 运行时环境中的严重错误。Error 通常是指无法恢复或无法处理的错误,例如 OutOfMemoryError(内存溢出错误)、StackOverflowError(栈溢出错误)等。与 RuntimeException 不同,Error 类型的异常通常不需要捕获或处理,因为它们表示严重的系统问题,需要通过重新启动应用程序或修复环境来解决。

而进行如下修改后,就表示所有的异常都会回滚。 

 2.propagation

  1. REQUIRED(默认):如果当前存在事务,则加入该事务,如果不存在事务,则创建一个新的事务。

  2. REQUIRES_NEW:无论当前是否存在事务,都会创建一个新的事务。如果当前存在事务,则将其挂起。

  3. SUPPORTS:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行。

  4. MANDATORY:如果当前存在事务,则加入该事务;如果不存在事务,则抛出异常。

  5. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将其挂起。

  6. NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常。

  7. NESTED:如果当前存在事务,则在一个嵌套的事务中执行;如果当前不存在事务,则创建一个新的事务。

最常见的是1和2,我们用一个例子来简单说明一下这两个:

首先,我们假设有一个UserService类,其中包含两个方法:methodA()methodB()。假设这两个方法都被声明为带有@Transactional注解的事务方法。

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    public void methodA() {
        // 执行一些数据库操作
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 执行一些数据库操作
    }
}

在这个例子中,methodA()使用了默认的事务传播行为REQUIRED,而methodB()使用了传播行为REQUIRES_NEW

现在,假设我们在一个服务类中调用这两个方法:

@Service
public class TransactionDemoService {
    
    @Autowired
    private UserService userService;
    
    @Transactional
    public void performTransactionDemo() {
        userService.methodA();
        userService.methodB();
    }
}

在这个示例中,performTransactionDemo()方法也被声明为带有@Transactional注解的事务方法。

现在,当我们调用performTransactionDemo()方法时,以下是事务的行为:

  1. userService.methodA()方法被调用时,如果当前存在事务,则methodA()将加入该事务;如果不存在事务,则将创建一个新的事务。

  2. userService.methodB()方法被调用时,无论当前是否存在事务,methodB()都将创建一个新的事务。如果当前存在事务,则将其挂起。

通过这个示例,我们可以看到REQUIREDREQUIRES_NEW事务传播行为的区别。REQUIRED会共享同一个事务,而REQUIRES_NEW会创建一个独立的新事务,与外部事务无关。

反映到这个代码中,就是A会添加到performTransactionDemo事务中,如果performTransactionDemo()事务回滚,那么A也会回滚,因为两个共用一个事务,而B是新建一个事务,他和performTransactionDemo()并不在同一个事务里面,并不会随着performTransactionDemo()事务的回滚而回滚。

 总结:

当涉及到数据操作时,事务管理是非常重要的。Spring框架提供了强大而灵活的事务管理功能,使开发人员能够轻松地在应用程序中实现数据一致性和可靠性。

本文介绍了Spring框架中常见的事务管理概念和技术,包括事务传播行为。

  • 事务是一组数据库操作的逻辑单元,要么全部成功提交,要么全部回滚。它确保数据的一致性和可靠性。

  • Spring中的事务管理依赖于底层的事务管理器,例如JDBC事务管理器或JTA事务管理器。这样,我们可以灵活地选择适合应用程序需求的事务管理器。

  • Spring提供了两种配置事务的方式:基于注解的配置和基于XML的配置。基于注解的配置更为常用和简洁,通过在方法上添加@Transactional注解来声明事务属性。

  • 在Spring中,事务传播行为决定了事务如何在方法调用之间传播和管理。常见的传播行为包括REQUIREDREQUIRES_NEWSUPPORTSMANDATORYNOT_SUPPORTEDNEVERNESTED

总而言之,Spring事务管理提供了一种简单且强大的方式来管理数据库事务。通过有效地利用事务传播行为,我们可以确保在应用程序中的多个方法调用或数据库操作之间正确地管理和控制事务。这使得我们能够实现数据的一致性和可靠性,同时保持代码的简洁和可维护性。

Spring的事务管理功能是众多企业级应用程序中不可或缺的一部分,对于构建稳健和可靠的应用程序是至关重要的。希望本文能够帮助读者理解并正确使用Spring中的事务管理机制。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

69e9169c980f43e0aad31ff9ada88a9c.png

 

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

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

相关文章

腾讯云服务器简介_CVM优势_常见问题解答

腾讯云服务器CVM提供安全可靠的弹性计算服务,腾讯云明星级云服务器,弹性计算实时扩展或缩减计算资源,支持包年包月、按量计费和竞价实例计费模式,CVM提供多种CPU、内存、硬盘和带宽可以灵活调整的实例规格,提供9个9的数…

Dubbo-CVE-2020-1948

APache Dubbo简介 Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需要用的,只有在分布式…

四.Docker容器技术

课程内容 DevOps和云原生Docker基础命令Docker安装软件Docker项目部署 一.DevOps和云原生 1.微服务的痛点 再来看一下我们的微服务架构 , 每个组件都需要服务器去部署,加起来可能需要几十个甚至上百个服务器。 这样的微服务项目在部署上会遇到什么问…

【重拾C语言】六、批量数据组织(一)数组(数组类型、声明与操作、多维数组;典例:杨辉三角、矩阵乘积、消去法)

目录 前言 六、批量数据组织——数组 6.1 成绩统计——数组类型 6.1.1 数组类型 6.1.2 数组声明与操作 6.1.3 成绩统计 6.2 统计多科成绩——多维数组 6.3 程序设计实例 6.3.1 杨辉三角形 6.3.2 矩阵乘积 6.3.3 消去法 6.4 线性表——分类与检索 前言 ChatGPT C语…

Unity 致社区公开信,调整 runtime fee 政策

导读面对此前制定的 "Funtime Fee" 收费政策所引发的用户争议与不满,Unity Create 负责人 Marc Whitten 正式发布了一封致社区的道歉公开信,并详细解释了其定价策略的变更。 我想以此开始:对不起。 在宣布新的 Runtime Fee 政策之…

【Spring内容进阶 | 第三篇】AOP进阶内容

前言: 在前面我们已经粗略的介绍了什么是AOP以及各种基础知识点,而本篇我们将聚焦于AOP的细节,详细的讲解一下AOP中的通知类型,通知顺序,切入点表达式以及连接点。通过对AOP的熟练掌握,我们可以快速编写出低…

【类加载子系统】

文章目录 1. 内存结构2. 类加载器与类的加载过程3. 类加载器分类4. 双亲委派机制 1. 内存结构 Class 文件类加载子系统运行时数据区 方法区堆程序计数器虚拟机栈本地方法栈 执行引擎本地方法接口本地方法库 2. 类加载器与类的加载过程 类加载器子系统负责从文件系统或者网络中加…

基于树种优化的BP神经网络(分类应用) - 附代码

基于树种优化的BP神经网络(分类应用) - 附代码 文章目录 基于树种优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.树种优化BP神经网络3.1 BP神经网络参数设置3.2 树种算法应用 4.测试结果:5.M…

旅美经济学家向凌云在美获对冲基金之父威廉·雅克颁授美国文化奖

2023年9月22日,第三届LABA偶像奖(LABA ICON AWARDS )「美国文化奖」颁奖典礼在美国洛杉矶比佛利山庄四季酒店举办。美国本土、欧洲、亚洲都有众多艺术家、经济学家和各行业优秀人才积极参与其中。 对冲基金之父威廉雅克(William Jacques)向经济学家Dr. Lingyun Xiang 向凌云颁发…

【进程管理】认识系统调用函数fork

目录 前言 一.查看进程的pid 二.父子进程 三.查看进程的第二种方式 四.代码创建进程——fork 1.fork的功能 2.fork的返回值 3.fork代码的一般写法 五.对于fork的理解 1.fork干了些什么? 2.fork为什么给子进程返回0,给父进程返回子进程的pid&…

大数据与Hadoop入门理论

一、大数据的3种数据类型 1、结构化数据 可定义,有类型、格式、结构的强制约束 如:RDBMS(关系型数据库管理系统) 2、非结构化数据 没有规律没有数据约束可言,很复杂难以解析 如:文本文件,视…

第86步 时间序列建模实战:Transformer回归建模

基于WIN10的64位系统演示 一、写在前面 这一期,我们介绍Transformer回归。 同样,这里使用这个数据: 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal Sy…

Casdoor系统static任意文件读取漏洞

文章目录 Casdoor系统static任意文件读取漏洞复现0x01 前言0x02 漏洞描述0x03 影响平台0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 Casdoor系统static任意文件读取漏洞复现 0x01 前言 免责声明:请勿利用文章内的相关技术从事非法测试,由…

郁金香2021年游戏辅助技术中级班(七)

郁金香2021年游戏辅助技术中级班(七) 058-C,C写代码HOOK分析封包数据格式A059-C,C写代码HOOK分析封包数据格式B-detours劫持060-C,C写代码HOOK分析封包数据格式C-过滤和格式化061-C,C写代码HOOK分析封包数据格式D-写入配置文件062-C,C写代码HOOK分析封包…

容器运行elasticsearch安装ik分词非root权限安装报错问题

有些应用默认不允许root用户运行,来确保应用的安全性,这也会导致我们使用docker run后一些操作问题,用es安装ik分词器举例(es版本8.9.0,analysis-ik版本8.9.0) 1. 容器启动elasticsearch 如挂载方式&…

第二证券:A股“业绩底”已现?两大板块被看好

9月,A股商场经历了一段明显的缩量下跌,成交量持续萎缩,增量资金不足,商场“痛感”激烈。跟着国庆十一长假结束,2023年四季度正式敞开,大都分析人士和私募安排都认为,国内经济预期转好将为A股商场…

蓝桥杯每日一题2023.10.7

跑步锻炼 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 简单枚举&#xff0c;对于2的情况特判即可 #include<bits/stdc.h> using namespace std; int num, ans, flag; int m[13] {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; bool is_ren(int n) {if((n %…

3d渲染农场全面升级,好用的渲染平台值得了解

什么是渲染农场&#xff1f; 渲染农场是专门从事 3D 渲染的大型机器集合&#xff0c;称为渲染节点&#xff0c;这些机器组合在一起执行一项任务&#xff08;渲染 3D 帧和动画&#xff09;。通过将渲染工作分配给数百台机器&#xff0c;可以显着减少渲染时间&#xff0c;从而使…

STM32F030在使用内部参考电压 (VREFINT)时与STM32G070的区别

背景&#xff1a; 之前使用过STM32G070的内部参考电压来提升ADC采集的准确度&#xff08;STM32使用内部参考电压提高ADC采集准确度&#xff09;&#xff0c;所以本次使用STM32F030的芯片时直接把之前G070的代码拿过来用了&#xff0c;但是出现了问题。 查找资料发现两者不同&am…

go语言判断管道是否关闭的误区

前言 本文是探讨的是"在Go语言中&#xff0c;我们是否可以使用读取管道时的第二个返回值来判断管道是否关闭?" 样例 在Go语言中&#xff0c;我们是否可以使用读取管道时的第二个返回值来判断管道是否关闭? 可以看下面的代码 package mainimport "fmt"…