轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

  • Spring

    • 基础

    • 框架

      • Spring容器初始化过程和Bean生命周期探究
      • Spring容器:从依赖管理到注解配置全解析
      • Spring事件探秘
      • Spring AOP的应用
      • Spring 事务管理
        • 一、事务基础知识
          • 1、什么是事务
          • 2、并发请求导致的各种问题
          • 3、事务隔离机制
          • 4、JDBC事务管理
        • 二、Spring事务管理
          • 1、Spring事务管理的概念
          • 2、事务管理器
          • 3、@Transactional注解
          • 4、自定义事务注解
          • 5、事务的隔离与传播
          • 6、事务的超时与只读
          • 7、事务的回滚
          • 8、AOP方式控制事务
          • 9、事务中的事件通知
          • 10、编程式事务管理之TransactionTemplate
        • 三、总结
      • Spring中的资源访问:Resource接口
      • Spring中的验证、数据绑定和类型转换
      • Spring表达式语言(SpELl)
      • Spring中的属性占位符
      • Spring数据缓冲区与编解码器详解
      • Spring对于原生镜像和AOT的支持
      • Spring中的数据访问:JDBC、R2DBC、ORM、Object-XML
      • Spring中的Web访问:Servlet API支持
      • Spring中的Web访问:WebSocket支持
      • Spring中的Web访问:响应式栈 WebFlux
      • Spring中的集成测试与单元测试
      • Spring与多种技术的集成
      • Spring框架版本新特性
    • Spring Boot

    • 集成

  • 其他语言

  • 工具

  • 后端
  • Spring
  • 框架
轩辕李
2023-08-19
目录

Spring 事务管理

# 一、事务基础知识

# 1、什么是事务

事务是指数据库中一组操作被视为一个单独的执行单元,这组操作要么全部执行成功,要么全部回滚,保证数据的一致性和完整性。

事务的存在有以下几个原因:

  1. 数据的一致性:事务可以确保在数据库中的操作是一致的,即满足预定义的约束条件。事务将一组操作作为一个整体,要么全部执行成功,要么全部回滚,避免了数据的不一致性。
  2. 数据的完整性:事务可以保护数据的完整性,即在事务执行过程中,数据库的状态始终保持一致。如果事务执行过程中出现错误或中断,可以通过回滚操作将数据库状态还原到事务开始之前的状态。
  3. 并发控制:在并发环境下,多个用户可能同时对数据库进行读写操作,事务提供了并发控制的机制,确保并发操作不会产生不一致的结果。通过隔离级别和锁机制,事务可以提供一定程度的并发控制,避免并发操作引发的并发问题,如脏读、不可重复读和幻读。
  4. 效率和性能:事务可以减少对数据库的访问次数,提高数据库的效率。将多个操作合并为一个事务,可以减少与数据库的通信开销,提高数据操作的效率。

事务具有以下特性(ACID):

  • 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败回滚。
  • 一致性(Consistency):事务执行前后,数据库的状态保持一致。
  • 隔离性(Isolation):事务的执行不受其他事务的干扰。
  • 持久性(Durability):事务一旦提交,对数据库的修改将永久保存。

# 2、并发请求导致的各种问题

为了符合ACID原则,常见的数据库都会提供事务控制机制。

但是,即使在有事务控制机制的情况下。事务并发执行过程中依然可能存在以下问题:

  • 脏读(Dirty Read):一个事务读取了另一个未提交事务的数据。
  • 不可重复读(Non-repeatable Read):在一个事务内,多次读取同一数据,但结果不一致。
  • 幻读(Phantom Read):在一个事务内,多次查询同一范围的数据,结果却不一致。
  • 第一类丢失更新(First Lost Update):当两个或多个并发事务同时对同一数据进行更新时,只有一个事务的更新操作能够成功,其他事务的更新操作将被覆盖,导致部分数据的更新丢失。
  • 第二类丢失更新(Second Lost Update):当两个或多个并发事务读取同一数据并进行计算后再写回数据库时,在没有事务控制的情况下,可能出现其中一个事务的计算结果被覆盖的情况,导致部分计算结果的丢失。

# 3、事务隔离机制

事务隔离机制是指在多个事务并发执行时,保证各个事务之间的操作互不干扰,每个事务都感觉到自己是独立执行的。事务隔离级别定义了事务之间的隔离程度。

常见的事务隔离级别包括:

  • READ_UNCOMMITTED(读未提交):最低级别,允许读取未提交的数据,存在脏读、不可重复读和幻读的问题。
  • READ_COMMITTED(读已提交):保证读取到的数据是已提交的,解决了脏读问题,但不可重复读和幻读仍可能出现。
  • REPEATABLE_READ(可重复读):保证事务执行期间多次读取同一数据的结果是一致的,解决了脏读和不可重复读问题,但幻读仍可能出现。
  • SERIALIZABLE(串行化):最高级别,事务串行执行,避免了所有并发问题,但性能较差。

下面是四种常见的事务隔离级别(隔离机制)对于并发问题的解决情况的表格:

问题 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新
读未提交(Read Uncommitted) 可能发生 可能发生 可能发生 不会发生 可能发生
读已提交(Read Committed) 不会发生 可能发生 可能发生 不会发生 可能发生
可重复读(Repeatable Read) 不会发生 不会发生 可能发生 不会发生 不会发生
串行化(Serializable) 不会发生 不会发生 不会发生 不会发生 不会发生

在MySQL中,可以通过如下语句查询默认的事务隔离级别:

# 全局默认
SHOW VARIABLES LIKE 'transaction_isolation';

# session默认
SELECT @@session.transaction_isolation;

# 4、JDBC事务管理

参考之前的文章:JDBC 事务管理

# 二、Spring事务管理

# 1、Spring事务管理的概念

Spring框架提供了强大的事务管理功能,可以帮助开发者管理和控制应用程序中的事务。下面是Spring事务管理的一些关键概念:

  1. 事务:事务是一组操作作为一个不可分割的工作单元,要么全部成功提交,要么全部回滚。在Spring中,事务可以在方法级别或类级别进行定义,被注解或配置进行标识。

  2. 事务管理器(Transaction Manager):事务管理器是Spring框架提供的用于管理和控制事务的接口。它负责管理事务的开始、提交、回滚和回滚点等操作,与具体的数据访问技术(如JDBC、Hibernate)进行交互。

  3. 事务定义(Transaction Definition):事务定义用于定义事务的属性,包括隔离级别、传播行为、超时时间等。可以通过编程方式或声明式方式进行定义。

  4. 事务传播行为(Transaction Propagation):事务传播行为定义了在方法调用链中的事务如何传播和交互。例如,如果一个方法A调用了另一个方法B,事务传播行为规定了B方法是否在A方法的事务中执行,或者创建一个新的独立事务。

  5. 事务隔离级别(Transaction Isolation Level):事务隔离级别定义了在并发环境中事务之间的隔离程度,以避免并发问题(如脏读、不可重复读、幻读)。常见的隔离级别包括读未提交、读已提交、可重复读和串行化。

  6. 事务切面(Transaction Aspect):Spring通过AOP(面向切面编程)实现事务管理。事务切面是将事务管理逻辑与业务逻辑分离的关键组件,它通过在方法调用前后进行拦截,并在合适的时机开启、提交或回滚事务。

  7. 声明式事务管理:Spring提供了声明式事务管理的功能,通过注解或XML配置的方式声明事务的属性,而无需显式编写事务管理的代码。这种方式可以将事务管理与业务逻辑解耦,使代码更加简洁和可维护。

通过Spring的事务管理,开发者可以方便地控制事务的边界和行为,保证数据的一致性和完整性,并提供并发控制的支持,提高应用程序的可靠性和性能。

# 2、事务管理器

事务管理器负责管理和控制事务的开始、提交、回滚等操作。Spring支持多种事务管理器,如DataSourceTransactionManager(用于JDBC事务)和HibernateTransactionManager(用于Hibernate事务)等。

在Spring中配置事务管理器的代码取决于你使用的具体的数据访问技术。下面是两个常见的配置示例:

  1. 配置JDBC事务管理器(使用DataSourceTransactionManager):
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;

@Configuration
public class AppConfig {
    
    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    
    // 其他配置代码...
}

在上述示例中,我们使用了DataSourceTransactionManager作为JDBC事务管理器。我们将DataSource对象注入到transactionManager方法中,并将其作为参数传递给DataSourceTransactionManager的构造函数。最后,我们将DataSourceTransactionManager对象声明为一个@Bean,以便Spring可以将其托管。

  1. 配置Hibernate事务管理器(使用HibernateTransactionManager):
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import javax.sql.DataSource;

@Configuration
public class AppConfig {
    
    @Bean
    public HibernateTransactionManager transactionManager(LocalSessionFactoryBean sessionFactory) {
        return new HibernateTransactionManager(sessionFactory.getObject());
    }
    
    // 其他配置代码...
}

在上述示例中,我们使用了HibernateTransactionManager作为Hibernate事务管理器。我们将LocalSessionFactoryBean对象注入到transactionManager方法中,并通过调用sessionFactory.getObject()方法获取SessionFactory对象,然后将其传递给HibernateTransactionManager的构造函数。最后,我们将HibernateTransactionManager对象声明为一个@Bean。

需要注意的是,你还需要在配置中提供适当的数据源(DataSource)和Hibernate的SessionFactory。具体的配置方式取决于你的项目和数据访问技术的选择。

# 3、@Transactional注解

Spring提供了@Transactional注解来声明事务通知。通过在方法上添加@Transactional注解中定义事务通知,可以将方法标记为需要进行事务管理的。

下面是使用@Transactional注解配置事务通知的示例:

import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {
    
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, timeout = 10)
    public void performTransactionalOperation() {
        // 执行事务性操作
    }
    
    // 其他业务方法...
}

在上述示例中,我们将@Transactional注解应用于performTransactionalOperation()方法上。这将告诉Spring该方法需要在事务中执行,如果事务不存在,则会自动创建一个新的事务。当该方法执行完毕时,Spring会根据事务管理器的配置来决定是否提交事务或回滚事务。

我们使用rollbackFor属性设置回滚的触发异常(默认是RuntimeException),使用isolation属性设置事务隔离级别为READ_COMMITTED,使用propagation属性设置事务传播行为为REQUIRED,使用timeout属性设置事务超时时间为10秒。

# 4、自定义事务注解

@Transactional注解的value属性可以配置不同的事务管理器,比如:

@Transactional(value = "forumTransactionManager")
public void performTransactionalOperation() {
    // 执行事务性操作
}

如果我们需要在多个方法中使用同一个事务管理器,那么每个方法都需要添加@Transactional(value = "forumTransactionManager")注解,这样会导致代码重复。

为了避免代码重复,我们可以自定义一个事务注解,然后在需要的地方使用该注解。下面是自定义事务注解的示例:

假设已经存在forumTransactionManager事务管理器。

接下来,我们可以定义一个自定义的事务注解@CustomTransactional,代码如下:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(value = "forumTransactionManager")
public @interface CustomTransactional {
}

在上面的代码中,我们使用了@Transactional注解,并指定了value属性为forumTransactionManager,表示使用自定义事务管理器CustomTransactionManager。

接下来,我们可以在需要的方法上使用@CustomTransactional注解,而不需要再指定具体的事务管理器了,示例如下:

@CustomTransactional
public void performTransactionalOperation() {
    // 执行事务性操作
}

这样就可以避免代码重复,统一使用自定义的事务管理器。

# 5、事务的隔离与传播

在第一节的事务隔离机制中,我们已经介绍了事务隔离的四种级别。在Spring中,我们可以通过@Transactional注解的isolation属性来设置事务的隔离级别。

@Transactional注解还有一个propagation属性,用于设置事务的传播行为。事务的传播行为定义了在方法调用链中的事务如何传播和交互。例如,如果一个方法A调用了另一个方法B,事务传播行为规定了B方法是否在A方法的事务中执行,或者创建一个新的独立事务。

@Transactional注解的propagation属性支持以下7种事务传播行为:

  1. Propagation.REQUIRED:默认值。如果当前没有事务,就创建一个新事务;如果已经存在一个事务中,就加入到这个事务中。

  2. Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行。

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

  4. Propagation.REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则挂起当前事务。

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

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

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

这些事务传播行为可以根据具体的业务需求进行选择和配置。默认情况下,@Transactional注解的propagation属性值为Propagation.REQUIRED,即默认使用的是REQUIRED传播行为。

# 6、事务的超时与只读

@Transactional注解还有两个属性:timeout和readOnly,他们分别用于设置事务的超时时间和只读属性。

  1. timeout属性用于设置事务的超时时间,单位是秒。如果事务执行时间超过了指定的超时时间,则事务会被自动回滚。示例代码如下:
@Transactional(timeout = 10)
public void performTransactionalOperation() {
    // 执行事务性操作
}

在上面的示例中,performTransactionalOperation方法的事务超时时间被设置为10秒。

  1. readOnly属性用于设置事务的只读属性。如果将readOnly属性设置为true,表示当前事务只读,不会对数据库进行修改操作,这样可以提高事务的性能。示例代码如下:
@Transactional(readOnly = true)
public void performTransactionalOperation() {
    // 执行只读操作
}

在上面的示例中,performTransactionalOperation方法的事务被设置为只读。如果在只读事务中进行了修改操作,会抛出异常。

# 7、事务的回滚

在默认情况下,当被@Transactional注解标记的方法抛出任何未被捕获的异常时,事务会自动回滚。

事务的回滚是通过抛出异常来触发的。当方法抛出一个未被捕获的异常时,事务管理器会将当前事务标记为回滚状态,并抛出一个RuntimeException或其子类的异常。这样,事务管理器会回滚所有已执行的数据库操作,包括之前成功执行的操作。

以下是一个示例代码,演示了在方法中使用@Transactional注解,并触发事务回滚的情况:

@Transactional
public void performTransactionalOperation() {
    // 执行事务性操作
    // 抛出一个未被捕获的异常
    throw new RuntimeException("Something went wrong");
}

在上述示例中,当performTransactionalOperation方法抛出RuntimeException时,事务管理器会将当前事务回滚。所有在该方法中执行的数据库操作都会被撤销。

需要注意的是,只有抛出RuntimeException及其子类的异常才会触发事务回滚。如果抛出的异常是受检异常(checked exception),例如IOException,事务管理器不会自动回滚事务。如果希望在遇到受检异常时也触发事务回滚,可以在@Transactional注解上添加rollbackFor属性来指定需要回滚的异常类型。例如:

@Transactional(rollbackFor = IOException.class)
public void performTransactionalOperation() throws IOException {
    // 执行事务性操作
    // 抛出一个受检异常
    throw new IOException("Something went wrong");
}

在上述示例中,当performTransactionalOperation方法抛出IOException时,事务管理器会将当前事务回滚。

如果希望在遇到任何异常时都触发事务回滚,可以在@Transactional注解上添加rollbackFor属性,并将其设置为Exception.class。例如:

@Transactional(rollbackFor = Exception.class)
public void performTransactionalOperation() throws IOException {
    // 执行事务性操作
    // 抛出一个受检异常
    throw new IOException("Something went wrong");
}

当然,你也可以排除不需要回滚的异常,需要用到noRollbackFor属性。例如:

@Transactional(rollbackFor = Exception.class, noRollbackFor = IOException.class)
public void performTransactionalOperation() throws IOException {
    // 执行事务性操作
    // 抛出一个受检异常
    throw new IOException("Something went wrong");
}

# 8、AOP方式控制事务

在Spring中,你可以使用切面配置的方式对某些类中的某些方法进行事务控制,而不是使用@Transactional注解。

首先,创建一个配置类,用于定义事务管理器和事务通知。示例代码如下:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Autowired
    private DataSource dataSource;

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

    @Bean
    public TransactionInterceptor transactionAdvice() {
        Properties attributes = new Properties();
        attributes.setProperty("save*", "PROPAGATION_REQUIRED");
        attributes.setProperty("update*", "PROPAGATION_REQUIRED");
        attributes.setProperty("delete*", "PROPAGATION_REQUIRED");
        attributes.setProperty("get*", "PROPAGATION_REQUIRED,readOnly");

        NameMatchTransactionAttributeSource attributeSource = new NameMatchTransactionAttributeSource();
        attributeSource.setProperties(attributes);

        return new TransactionInterceptor(transactionManager(), attributeSource);
    }

    @Bean
    public Advisor transactionAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* com.example.MyService.*(..))");

        return new DefaultPointcutAdvisor(pointcut, transactionAdvice());
    }

}

在上述示例中,transactionManager方法定义了事务管理器。transactionAdvice方法定义了事务通知,通过Properties对象设置了事务的属性。transactionAdvisor方法定义了事务通知的切入点和通知对象的关联。

在transactionAdvice方法中,你可以根据自己的需求来定义事务规则,并使用不同的传播行为和只读属性来控制事务的行为。

# 9、事务中的事件通知

在Spring事件探秘-事务绑定事件中,我们能看到事务方法中发送事件存在的问题。

除了使用@TransactionalEventListener注解可以解决上面的问题,你也可以使用编码式的方式手动控制在事务执行成功后的逻辑:

        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    SpringContextHolder.getApplicationContext().publishEvent(new SomeEvent(t));
                }
            });
        } else {
            SpringContextHolder.getApplicationContext().publishEvent(new SomeEvent(t));
        }

上述代码中,首先判断当前是否在事务方法中。如果是的话,则注册此事务提交成功后的执行逻辑;否则,则正常发送事件。

# 10、编程式事务管理之TransactionTemplate

在实际应用中,很少通过编程进行事务管理。但Spring还是提供了TransactionTemplate模板类来满足一些特殊场合的需求。

TransactionTemplate是Spring框架提供的编程式事务管理的工具类。通过使用TransactionTemplate,我们可以在需要的地方对事务进行手动控制。

以下是使用TransactionTemplate进行编程式事务管理的示例:

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

public class TransactionalService {
    
    private TransactionTemplate transactionTemplate;
    
    // 依赖注入TransactionTemplate
    public TransactionalService(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    
    public void performTransactionalOperation() {
        transactionTemplate.execute(new TransactionCallback<Void>() {
            public Void doInTransaction(TransactionStatus status) {
                try {
                    // 在事务中执行数据库操作
                    // ...

                    // 如果需要,可以手动控制事务的回滚
                    // if (someErrorCondition) {
                    //     status.setRollbackOnly();
                    // }

                    // 返回结果
                    return null;
                } catch (Exception ex) {
                    // 如果发生异常,手动设置事务回滚
                    status.setRollbackOnly();
                    throw ex;
                }
            }
        });
    }
}

在上述示例中,我们首先通过构造函数注入了TransactionTemplate实例。然后,在performTransactionalOperation()方法中,我们使用transactionTemplate.execute()方法来执行一个事务操作。

transactionTemplate.execute()方法接受一个TransactionCallback对象作为参数,其中的doInTransaction()方法定义了在事务中执行的逻辑。在doInTransaction()方法中,我们可以执行数据库操作、处理异常、手动控制事务的回滚等。

如果在事务过程中发生异常,我们可以手动调用status.setRollbackOnly()方法将事务标记为回滚。这样,在事务结束时,Spring会检查事务状态,并根据标记决定是否回滚事务。

需要注意的是,TransactionTemplate会自动管理事务的开始、提交和回滚等操作,我们不需要手动调用这些方法。

通过使用TransactionTemplate,我们可以更灵活地控制事务的边界和行为,以及处理事务中的异常。它适用于那些需要在代码中动态决定事务的范围和行为的场景。

# 三、总结

本文中,我们主要介绍了事务基础知识和Spring事务管理相关的内容。

在事务基础知识部分,文章首先介绍了事务的定义,即一组数据库操作要么全部成功,要么全部失败。然后讨论了并发请求可能导致的各种问题,如脏读、不可重复读和幻读。接着介绍了事务隔离机制,包括读未提交、读已提交、可重复读和串行化四个隔离级别。最后介绍了JDBC事务管理的相关知识。

在Spring事务管理部分,文章首先解释了Spring事务管理的概念,即通过对业务方法的切面来控制事务的提交和回滚。然后介绍了事务管理器的作用,接着介绍了@Transactional注解的使用,以及如何自定义事务注解。然后讨论了事务的隔离与传播,包括设置隔离级别和传播行为。接下来介绍了事务的超时与只读的设置,以及如何进行事务的回滚。最后介绍了使用AOP方式控制事务的方法,以及编程式事务管理中的TransactionTemplate的使用。

本文详细介绍了事务基础知识和Spring事务管理的相关内容,希望对于你理解事务的概念和使用Spring进行事务管理的方法有所帮助。

祝你变得更强!

编辑 (opens new window)
#Spring事务
上次更新: 2025/04/01
Spring AOP的应用
Spring中的资源访问:Resource接口

← Spring AOP的应用 Spring中的资源访问:Resource接口→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式