Java Spring 事物处理

一、定义

事务(Transaction)是指作为单个逻辑工作单元执行的一系列操作。操作要么全部成功执行,要么全部失败回滚,以确保数据的一致性和完整性。

二、特性

  1. 原子性(Atomicity):事务被视为不可分割的工作单元,要么全部执行成功,要么全部失败回滚。即使在执行过程中发生了错误,事务也会被回滚到最初的状态,不会留下部分执行的结果。

  2. 一致性(Consistency):事务在执行前后,数据库中的数据应该保持一致性。这意味着事务的执行不会破坏数据库的完整性约束,如唯一性约束、外键约束等。

  3. 隔离性(Isolation):多个事务并发执行时,每个事务的操作应该相互隔离,互不干扰。即使多个事务同时对同一数据进行操作,也不应该相互影响,以避免数据的不一致性。

  4. 持久性(Durability):一旦事务执行成功提交,其结果应该对其他事务可见,并且应该持久保存在系统中,即使系统发生故障或重启,事务的提交结果也不会丢失。

在编程中,特别是在数据库操作中,事务通常用于确保对数据库的一系列操作是原子性的,要么全部成功提交,要么全部回滚。这有助于确保数据的完整性和一致性。

三、事物处理方式

Spring 框架提供了多种方式来处理事务,包括声明式事务管理和编程式事务管理。

  • 声明式事务管理:Spring 声明式事务管理在底层采用了 AOP 技术,其最大的优点在于无须通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。
    其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

  • 编程式事务管理:除了声明式事务管理外,Spring 框架还提供了编程式事务管理的方式。通过使用 TransactionTemplatePlatformTransactionManager 接口,开发人员可以在代码中显式地控制事务的开始、提交和回滚。

四、声明式事务管理注解方式

1. 启用 Spring 的注释驱动事务管理功能

@EnableTransactionManagement 是 Spring 框架提供的一个注解,用于启用注解驱动的事务管理。一旦 @EnableTransactionManagement 注解被添加,Spring 容器将会自动扫描带有 @Transactional 注解的方法,并为其创建代理以支持事务管理。

在 Spring 应用程序中,@EnableTransactionManagement 注解可以被用于配置类(通常是一个带有 @Configuration 注解的类)和启动类(通常是带有 @SpringBootApplication 注解的类)。虽然在这两种情况下都能够启用注解驱动的事务管理,但它们之间存在一些细微的区别。

配置类上使用 @EnableTransactionManagement

  • @EnableTransactionManagement 注解被添加到一个专门的配置类上时,通常用于集中配置和管理事务相关的设置,包括数据源、事务管理器等。

  • 配置类可以包含其他与应用程序配置相关的 bean 定义,例如数据源、JPA 实体管理工厂、持久化单元等。

  • 这种方式使得整个应用程序的事务管理配置更加集中和清晰,同时也使得代码更易于维护和理解。

启动类上使用 @EnableTransactionManagement

  • @EnableTransactionManagement 注解被添加到 Spring Boot 的启动类上时,通常用于启用整个应用程序范围内的注解驱动的事务管理。

  • 启动类是 Spring Boot 应用程序的入口点,通过在启动类上添加 @EnableTransactionManagement 注解,可以方便地启用注解驱动的事务管理,并为整个应用程序启用事务支持。

  • 这种方式使得整个应用程序范围内的事务管理配置更加简单,适用于小型应用程序或需要全局事务支持的情况。

总的来说,无论是在配置类上使用 @EnableTransactionManagement 还是在启动类上使用 @EnableTransactionManagement,都可以有效地启用注解驱动的事务管理。选择使用哪种方式取决于应用程序的规模、结构和需求。如果需要更加灵活的事务管理配置,通常会选择在配置类上使用 @EnableTransactionManagement;而如果只需要简单地启用全局的事务支持,可以选择在启动类上使用 @EnableTransactionManagement。

2. 定义事务管理器

事务交给 spring 管理,那么你肯定要创建一个或者多个事务管理者,有这些管理者来管理具体的事务
在 Spring 应用程序中,要定义事务管理器,通常可以通过创建一个 bean 来配置事务管理器。事务管理器负责协调和管理数据库事务的提交和回滚。在 Spring 中,可以使用 PlatformTransactionManager 接口的实现来定义事务管理器,具体取决于应用程序使用的数据访问技术,比如 JDBC、JPA 或 Hibernate 等。

PlatformTransactionManager 多个实现类,用来应对不同的环境

  • JpaTransactionManager: 如果你用 jpa 来操作 db,那么需要用这个管理器来帮你控制事务。

  • DataSourceTransactionManager: 如果你用是指定数据源的方式,比如操作数据库用的是:JdbcTemplate、mybatis、ibatis,那么需要用这个管理器来帮你控制事务。

  • HibernateTransactionManager: 如果你用 hibernate 来操作 db,那么需要用这个管理器来帮你控制事务。

  • JtaTransactionManager: 如果你用的是 java 中的 jta 来操作 db,这种通常是分布式事务,此时需要用这种管理器来控制事务。

以下是一个简单的示例:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

3. 使用 @Transaction

在 Spring 框架中,@Transactional 注解用于声明一个方法是事务性的。通过在方法上添加 @Transactional 注解,开发人员可以指定该方法应该在一个事务中执行。这意味着,在方法执行期间,Spring 框架会自动管理事务的开始、提交、回滚等操作,以确保数据的一致性和完整性。

  • 方法级别使用:最常见的用法,放在 public 方法上,那么该方法将被 spring 自动加上事务。
  • 放在类上,那么当前类以及其下无限级子类中所有 pubilc 方法将被 spring 自动加上事务。
  • 放在接口上,那么接口的实现类中所有 public 都被 spring 自动加上事务。

@Transaction 参数说明

  1. value:指定事务管理器的 bean 名称,如果容器中有多事务管理器 PlatformTransactionManager,那么你得告诉 spring,当前配置需要使用哪个事务管理器

  2. transactionManager:同 value,value 和 transactionManager 选配一个就行,也可以为空,如果为空,默认会从容器中按照类型查找一个事务管理器 bean

  3. propagation:指定事务的传播行为。事务的传播行为定义了在不同事务边界内执行方法时,方法如何参与现有事务或者创建新的事务。可以设置为下列之一:

    • Propagation.REQUIRED:如果当前存在事务,则加入该事务;如果不存在,则创建一个新的事务。
      这是默认的传播行为,适用于大多数情况。它确保方法总是在一个事务中执行。

    • Propagation.REQUIRES_NEW:始终创建一个新的事务。
      使用这种传播行为,方法总是在一个新的事务中执行。即使调用方法的代码已经处于一个事务中,该方法也会挂起当前事务,并创建一个新的事务进行执行。

    • Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务的方式执行。
      这种传播行为表示方法可以在事务中执行,也可以在非事务的环境中执行。如果当前存在事务,则方法将加入该事务;如果没有事务,则方法以非事务的方式执行。

    • Propagation.NOT_SUPPORTED:以非事务的方式执行。如果当前存在事务,则将其挂起。
      使用这种传播行为,方法总是以非事务的方式执行。即使调用方法的代码已经处于一个事务中,该方法也会挂起当前事务,并以非事务的方式执行。

    • Propagation.MANDATORY:当前方法必须在一个事务中执行,否则会抛出异常。
      这种传播行为要求调用方法的代码必须在一个事务中执行,如果没有事务存在,则会抛出异常。

    • Propagation.NEVER:以非事务的方式执行。如果当前存在事务,则抛出异常。
      使用这种传播行为,方法总是以非事务的方式执行。如果调用方法的代码已经处于一个事务中,则会抛出异常。

    • Propagation.NESTED:如果当前存在事务,则在嵌套事务中执行;如果不存在事务,则创建一个新的事务。
      这种传播行为类似于 REQUIRED,但是可以在一个已存在的事务中创建一个嵌套事务。如果当前没有事务,则会创建一个新的事务。

  4. isolation:指定事务的隔离级别。事务隔离级别定义了在并发环境中,一个事务对数据的读取能力以及对其他事务修改的可见性。可以设置为下列之一:

    • Isolation.DEFAULT:使用数据库默认的隔离级别。通常情况下,数据库会使用一种默认的隔禅级别,这取决于具体的数据库系统。

    • Isolation.READ_UNCOMMITTED:一个事务可以读取另一个事务未提交的数据修改。这种隔离级别最低,不保证事务的一致性和隔离性。允许脏读、不可重复读和幻影读。

    • Isolation.READ_COMMITTED:一个事务只能读取另一个事务已经提交的数据修改。这种隔离级别可以避免脏读,但是仍然可能出现不可重复读和幻影读。

    • Isolation.REPEATABLE_READ:一个事务在多次读取同一数据时,能够保证读取到的数据是一致的。这种隔离级别可以避免脏读和不可重复读,但是仍然可能出现幻影读。

    • Isolation.SERIALIZABLE:最高的隔离级别,确保事务可以完全地隔离其他事务的并发操作,避免脏读、不可重复读和幻影读。这种隔离级别对数据库性能有较大的影响,因为可能需要对数据进行严格的锁定。

说明

  • 脏读(Dirty Read):脏读指的是一个事务读取了另一个事务未提交的数据。当一个事务对数据进行了修改但还未提交时,另一个事务读取到了这些未提交的数据,这就是脏读。如果第一个事务最终回滚了,那么另一个事务读取到的数据就是无效的。
    脏读会导致事务读取到不一致甚至无效的数据,破坏了事务的隔离性和一致性。

  • 不可重复读(Non-Repeatable Read):不可重复读指的是在一个事务内,多次读取同一数据时,得到了不同的结果。这是因为在事务执行期间,另一个事务对数据进行了修改并提交,导致第一个事务多次读取同一数据时得到了不同的结果。
    不可重复读会导致事务在同一数据上得到不一致的结果,也破坏了事务的隔离性和一致性。

  • 幻影读(Phantom Read):幻影读指的是在一个事务内,多次执行同一个查询,但得到了不同数量的数据。这是因为在事务执行期间,另一个事务对数据进行了插入或删除,导致第一个事务多次执行相同的查询时得到了不同数量的数据。

  • 幻影读会导致事务在相同的查询条件下得到不同数量的数据,同样破坏了事务的隔离性和一致性。

  1. timeout:指定事务的超时时间(以秒为单位)。如果事务在指定的时间内未能完成,则会被回滚。

  2. readOnly:指定事务是否为只读事务。如果设置为 true,则表示该事务只读取数据,不进行写操作,可以优化数据库性能。
    如果在一个标记为只读事务的方法中进行了数据更新、插入或删除操作,可能会导致事务提交失败或抛出异常。因此,在使用 readOnly 参数时,需要确保方法的实际操作与事务的只读属性相符。

  3. rollbackFor:指定哪些异常会触发事务回滚。可以设置为一个异常类型的数组,当发生这些异常之一时,事务会回滚。

  4. noRollbackFor:指定哪些异常不会触发事务回滚。同样可以设置为一个异常类型的数组。

  5. rollbackForClassName:与 rollbackFor 类似,但是接受异常类名的字符串数组,而不是实际的异常类。

  6. noRollbackForClassName:与 noRollbackFor 类似,但是接受异常类名的字符串数组,而不是实际的异常类。

这些参数可以在 @Transactional 注解中根据需要进行组合和配置,以便满足特定的事务管理需求。通过灵活地使用这些参数,可以精细地控制事务的行为和特性,以确保数据操作的一致性和完整性。

五、编程式事务管理方式

PlatformTransactionManager

在 Spring 框架中,PlatformTransactionManager 是用于控制事务的关键接口之一。它定义了一系列方法,用于管理事务的生命周期、提交事务以及回滚事务等操作。通过 PlatformTransactionManager 接口,我们可以在 Spring 应用中对事务进行精细化的控制。

以下是使用 PlatformTransactionManager 控制事务的一般步骤:

  1. 获取 PlatformTransactionManager 的实例。

  2. 利用获取的实例创建 TransactionDefinition 对象,该对象定义了事务的传播行为、隔禂级别、超时时间等属性。

  3. 利用 PlatformTransactionManager 开启事务,并获取 TransactionStatus 对象,该对象表示了当前事务的状态。

  4. 在事务内部执行需要进行事务管理的操作。

  5. 根据执行结果决定提交事务或者回滚事务。

下面是一个简单的示例:

import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.PlatformTransactionManager;

public class ProgrammaticTransactionExample {
    private PlatformTransactionManager transactionManager;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public void performProgrammaticTransaction() {
        // 定义事务的传播行为、隔禂级别等属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        // 开启事务
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // 在事务内部执行数据库操作
            // 如果发生异常,可以通过 status.setRollbackOnly() 进行事务回滚
            // 操作成功完成后,提交事务
            transactionManager.commit(status);
        } catch (Exception ex) {
            // 操作出现异常,回滚事务
            transactionManager.rollback(status);
        }
    }
}

TransactionTemplate

在 Spring 框架中,可以使用 TransactionTemplate 来实现编程式事务管理。TransactionTemplate 简化了编程式事务的操作,它封装了事务的开始、提交和回滚过程,使得在代码中控制事务变得更加方便。

以下是使用 TransactionTemplate 控制事务的一般步骤:

  1. 获取 PlatformTransactionManager 的实例。

  2. 创建 TransactionTemplate 实例,并将 PlatformTransactionManager 传入其构造函数。

  3. 通过 TransactionTemplate 提供的方法执行业务操作。

  • <T> T execute(TransactionCallback<T> action):有返回值,需要传递一个 TransactionCallback 对象,该对象定义了要在事务中执行的操作。
    TransactionCallback<T> 是一个函数式接口,执行时需要一个 TransactionStatus 类型的参数,以及返回个 T 类型的对象。
  • void executeWithoutResult(Consumer<TransactionStatus> action):没有返回值的,需传递一个 Consumer 对象,该对象定义了要在事务中执行的操作。
    Consumer<TransactionStatus> 是一个函数式接口,执行时需要一个 TransactionStatus 类型的参数,无返回结果。

源码中看的话 executeWithoutResult 方法是 TransactionOperations 接口中的一个默认方法 TransactionTemplate 实现了 TransactionOperations 接口,而在 TransactionOperations 接口中 executeWithoutResult 方法内其实是调用了 execute 方法执行的,只不过没有把 execute 方法的执行结果返回。
在使用这两种方法时,Spring 会自动管理事务的开始、提交和回滚过程,使得事务管理变得更加方便和简单。这样,您可以专注于业务逻辑的实现,而无需手动管理事务的细节。

下面是一个简单的示例:

import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class ProgrammaticTransactionWithTemplateExample {
    private PlatformTransactionManager transactionManager;
    private TransactionTemplate transactionTemplate;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public String execute() {
        return transactionTemplate.execute(status -> {
            // 在事务内部执行数据库操作
            // 如果发生异常,可以通过 status.setRollbackOnly() 进行事务回滚
            return "操作成功"; // 操作成功完成后返回
        });
    }
    public void executeWithoutResult() {
        transactionTemplate.executeWithoutResult(status -> {
            // 在事务内部执行数据库操作
            // 如果发生异常,可以通过 status.setRollbackOnly() 进行事务回滚
        });
    }
    
}
  • 29
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值