提到事务,这个我们应该比较熟悉了,在数据库学习的过程中,我们或多或少接触过了事务,当然你可能没有用到,也可能用到了,这篇博客我们将围绕Spring的相关事务的概念进行,了解Spring中的事务和事务可以用来解决什么问题。
什么是事务?
我们回顾一下我们当初学习数据库的时候的一些知识点:
MySQL事务
MySQL事务是由数据库管理系统(DBMS)实现的一种机制,主要用于管理对数据库的更改操作,如插入(INSERT)、更新(UPDATE)和删除(DELETE)。事务具有以下四个特性,通常被称为ACID特性:
作为单个逻辑工作单元执行的一组操作,要么全部成功执行,要么全部失败回滚,以确保数据的一致性和完整性。事务具有以下特性:
- 原子性(Atomicity):事务是一个不可分割的工作单元,要么全部执行成功,要么全部失败回滚,保证数据的完整性。
- 一致性(Consistency):事务执行前后,数据的状态保持一致,不会破坏数据的完整性约束。
- 隔离性(Isolation):多个事务同时执行时,每个事务的操作应该互相隔离,避免相互影响,确保数据操作的正确性。
- 持久性(Durability):一旦事务提交,其结果应该是永久性的,不会因为系统故障而丢失,数据应该持久保存在系统中。
MySQL的InnoDB存储引擎支持事务,而MyISAM等其他存储引擎则不支持。
Spring 事务
Spring框架提供了一个事务管理的抽象层,允许开发者以声明式或编程式的方式管理事务。Spring事务管理器可以与多种数据库和JPA/Hibernate等ORM框架协同工作。
Spring事务管理的关键组件是PlatformTransactionManager接口,它提供了开始、提交和回滚事务的方法。Spring还提供了一些额外的功能,如:
- 传播行为(Propagation Behavior):定义了事务方法被调用时的行为,例如是否在一个现有事务中运行,或者总是创建一个新的事务。
- 隔离级别(Isolation Level):允许开发者指定事务的隔离级别,从而控制并发操作的影响。
- 回滚规则(Rollback Rules):定义了哪些类型的异常会导致事务回滚。
- 只读事务(Read-Only Transactions):如果事务只包含读取操作,可以将其标记为只读,这样可以提高性能并减少锁定。
Spring事务和MySQL事务的区别
- 实现方式:
- MySQL事务由数据库本身实现,依赖于存储引擎(如InnoDB)。
- Spring事务由框架实现,可以调用底层数据库的事务机制,也可以提供额外的事务管理策略。
- 传播行为:
- MySQL事务不理解应用层面的事务传播行为,它只是执行SQL命令。
- Spring事务提供了对事务传播行为的支持,可以控制事务在不同方法调用间的传播方式。
- 回滚规则:
- MySQL事务只根据SQL语句的状态决定是否回滚,通常在未提交时遇到错误会回滚。
- Spring事务可以根据Java级别的异常来决定是否回滚事务,提供更细粒度的控制。
- 应用范围:
- MySQL事务主要关注事务的ACID特性和隔离级别。
- Spring事务除了ACID特性外,还提供了更多事务管理的高级特性,如隔离级别、传播行为、只读标志和超时控制。
Spring事务是在应用层面提供的一层抽象,用于更方便地管理和协调跨多个数据库操作的事务边界,而MySQL事务则是数据库层面的事务管理机制。在使用Spring框架进行开发时,通常会结合这两种事务管理方式。
Spring的事务类型
- 声明式事务管理:通过在配置文件或注解中声明事务的属性和行为,Spring框架会自动为方法添加事务管理,无需手动编写事务管理代码。
- 编程式事务管理:通过编写代码显式地控制事务的开始、提交、回滚等操作,可以更灵活地控制事务的边界和行为。
- 注解驱动事务管理:通过在方法上使用注解(如
@Transactional
)来声明事务的属性和行为,简化事务管理的配置和使用。 - 基于AspectJ的事务管理:通过AspectJ切面来实现事务管理,可以更细粒度地控制事务的切入点和行为。
- JTA事务管理:用于分布式事务管理,通过Java Transaction API(JTA)实现全局事务的管理和协调。
我们实际开发过程中,主要用的是声明式事务和编程式事务。所以我们在之后主要围绕的是声明式事务和编程式事务进行讲解。
准备工作
JdbcTemplate
JdbcTemplate是Spring框架提供的一个简化数据库操作的工具类,用于执行SQL查询、更新等操作。它封装了JDBC的繁琐操作,提供了更简洁、易用的API,同时处理了资源的释放和异常的捕获,使得数据库操作更加方便和安全。
通过JdbcTemplate,开发者可以使用模板方法来执行SQL语句,如查询单个结果、查询列表、更新数据等操作,而无需手动管理数据库连接、Statement、ResultSet等资源。同时,JdbcTemplate还支持命名参数、批处理、存储过程等高级功能,使得数据库操作更加灵活和高效。
项目目录结构
引入相关依赖
创建jdbc.properties
创建Spring-jdbc.xml
创建实体类
数据库自己创建根据实体类字段创建
业务层
控制层
测试类
编程式事务
Spring的编程式事务是通过编程的方式在代码中显式控制事务的开启、提交、回滚等操作的一种事务管理方式。相比声明式事务,编程式事务需要开发者在代码中显式调用事务管理相关的方法,实现对事务的控制。
编程式事务的主要特点包括:
- 事务管理器:开发者需要获取事务管理器,并通过事务管理器的方法来控制事务的开启、提交、回滚等操作。
- 事务边界:开发者需要在代码中明确定义事务的边界,即事务开始的位置和结束的位置,确保事务的正确执行。
- 事务控制:开发者可以根据业务需求在代码中灵活地控制事务的行为,如根据条件选择是提交事务还是回滚事务。
- 资源管理:开发者需要手动管理数据库连接、事务状态等资源,确保资源的正确释放和事务的一致性。
尽管编程式事务相对于声明式事务更为灵活,但也增加了代码的复杂性和维护成本。通常情况下,建议优先选择声明式事务,只有在特定需求下才考虑使用编程式事务。
事务功能的相关操作全部通过自己编写代码来实现:
编程式的实现方式存在缺陷:
- 细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
- 代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用。
声明式事务
Spring声明式事务是通过在方法或类上使用注解或XML配置的方式来定义事务的属性和行为,而无需在代码中显式编写事务管理逻辑。通过声明式事务,开发者可以将事务管理与业务逻辑分离,提高了代码的可维护性和可读性。
具体来说,Spring声明式事务的概念包括以下几个要点:
- 注解或XML配置:可以通过在方法或类上使用 @Transactional 注解或在XML配置文件中配置 tx:advice 等元素来声明事务的属性,如事务的传播行为、隔离级别、超时设置等。
- 事务切面:声明式事务通过AOP(面向切面编程)实现,将事务逻辑织入到目标方法的执行过程中。在方法调用前后,事务管理器会根据配置来开启、提交或回滚事务。
- 事务传播行为:定义了方法调用时事务如何传播的规则,如REQUIRED、REQUIRES_NEW、NESTED等,用于控制事务的边界和范围。
- 事务隔离级别:定义了事务之间的隔离程度,如READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE等,用于控制事务并发访问数据的方式。
- 事务管理器:声明式事务需要配置一个事务管理器,用于实际管理事务的提交、回滚等操作,常见的事务管理器包括DataSourceTransactionManager、JpaTransactionManager等。
既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。
封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作。
- 好处1:提高开发效率
- 好处2:消除了冗余的代码
- 好处3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方面的优化
所以,我们可以总结下面两个概念:
- 编程式:自己写代码实现功能
- 声明式:通过配置让框架实现功能