【Spring】5.Spring事务中的@Transactional注解剖析

发布于:2024-05-02 ⋅ 阅读:(24) ⋅ 点赞:(0)

事务是确保数据完整性的关键机制。Spring框架通过@Transactional注解提供了一种声明式事务管理的方式,极大地简化了事务的使用。在本篇文章中,我们将深入探讨Spring的@Transactional注解,包括它的工作原理、可用属性、如何配置以及在嵌套事务场景下的应用。此外,我们还将讨论将@Transactional注解应用于类与方法时的异同,以及如何选择适当的使用方式。

Spring的@Transactional注解

Spring的@Transactional是一个用于声明方法或类级别的事务属性的注解。它告诉Spring框架,被注解的方法应该在一个事务的上下文中执行。如果方法在事务中执行,那么该方法对数据库所做的更改要么全部成功提交,要么在发生异常时全部撤销。

工作原理

  1. 代理机制:Spring使用AOP(面向切面编程)来实现@Transactional注解。当Spring容器启动时,它扫描所有的@Transactional注解,并为被注解的方法创建一个代理。对于类级别的注解,类中的所有方法都会创建代理。

  2. 事务的创建和结束:当代理方法被调用时,Spring首先检查是否存在一个活动的事务。如果存在,代理方法会加入到这个事务中。如果不存在,Spring会创建一个新的事务。一旦方法执行完成,如果没有异常抛出,事务将被提交;如果发生异常,事务将被回滚。

  3. 事务管理器:Spring使用事务管理器(如DataSourceTransactionManagerJpaTransactionManager)来实际控制事务的创建、提交和回滚。

属性及配置

  1. propagation:定义事务的传播行为。

    • REQUIRED:默认值,加入已存在的事务或创建新事务。
    • SUPPORTS:支持事务但不是必须的;如果没有事务,就以非事务方式执行。
    • MANDATORY:必须在一个事务中执行,否则抛出异常。
    • REQUIRES_NEW:总是创建新事务,并将任何存在的事务挂起。
    • NOT_SUPPORTED:以非事务方式执行,任何存在的事务都会被挂起。
    • NEVER:必须不在事务中执行,如果存在事务则抛出异常。
    • NESTED:如果支持,则创建一个嵌套事务。
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    
  2. isolation:定义事务的隔离级别。

    • DEFAULT:使用后端数据库的默认隔离级别。
    • READ_UNCOMMITTED:最低隔离级别,允许读取未提交的数据。
    • READ_COMMITTED:保证读取已提交的数据。
    • REPEATABLE_READ:保证在一个事务中看到的数据保持不变。
    • SERIALIZABLE:最高隔离级别,完全串行化的事务。
    @Transactional(isolation = Isolation.READ_COMMITTED)
    
  3. timeout:定义事务的超时时间(秒)。如果事务在这个时间内没有完成,它将被自动回滚。

    @Transactional(timeout = 30)
    
  4. readOnly:指定事务是否为只读事务。这可以给数据库一个优化的提示。

    @Transactional(readOnly = true)
    
  5. rollbackFor:定义哪些异常类型会导致事务回滚。可以指定一个异常类型数组。

    @Transactional(rollbackFor = {RuntimeException.class, CustomException.class})
    
  6. noRollbackFor:定义哪些异常类型不会导致事务回滚。可以指定一个异常类型数组。

    @Transactional(noRollbackFor = NoRollbackException.class)
    
  7. phase:定义在事务的哪个阶段应用事务管理。通常与基于方法的事务管理相关。

    @Transactional(phase = TransactionPhase.BEFORE_COMMIT)
    
  8. transactionManager:指定使用的事务管理器的Bean名称。

    @Transactional(transactionManager = "transactionManagerBeanName")
    

通过合理配置这些属性,可以精确控制事务的行为,满足业务需求。

处理嵌套事务

嵌套事务通常在以下情况下需要进行处理:

  1. 细粒度控制:当您希望在一个大的事务中包含一些小的事务,并且这些小的事务可以独立于大事务进行回滚或提交时。
  2. 复杂业务逻辑:在一些复杂的业务逻辑中,可能需要在一个事务中执行多个步骤,其中某些步骤需要独立的事务控制。
  3. 性能优化:在某些情况下,使用嵌套事务可以减少锁争用,从而提高性能。

在Spring Boot中,处理嵌套事务需要使用支持嵌套事务的事务管理器。由于Spring Boot默认的DataSourceTransactionManager不支持嵌套事务,因此通常需要使用JTA事务管理器,如AtomikosBitronix

以下是在Spring Boot中使用@Transactional注解处理嵌套事务的步骤和示例代码:

1. 添加JTA事务管理器依赖

首先,需要添加JTA事务管理器的依赖。以Atomikos为例:

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

2. 配置JTA事务管理器

application.propertiesapplication.yml中配置JTA事务管理器:

# application.properties
spring.jta.enabled=true
spring.jta.log-dir=path/to/log/dir

3. 使用@Transactional注解处理嵌套事务

在服务层中,使用@Transactional注解声明外部事务和嵌套事务:

@Service
public class BusinessService {

    @Autowired
    private NestedService nestedService;

    @Transactional
    public void performBusinessOperation() {
        // 执行一些操作
        nestedService.performNestedOperation();
    }
}

@Service
public class NestedService {

    @Transactional(propagation = Propagation.NESTED, readOnly = true)
    public void performNestedOperation() {
        // 执行一些只读操作,这些操作在一个嵌套事务中
    }

    @Transactional(propagation = Propagation.NESTED, readOnly = false)
    public void performNestedOperationWithChange() {
        // 执行一些需要更改数据库的操作
        // 这些操作在一个嵌套事务中
    }
}

在这个例子中,BusinessService中的performBusinessOperation方法定义了一个外部事务。当调用NestedService中的performNestedOperationperformNestedOperationWithChange方法时,它们作为一个嵌套事务执行,因为它们也被@Transactional注解,且propagation属性设置为NESTED

注意事项:

  • 确保你的数据库和事务管理器都支持嵌套事务。
  • 使用嵌套事务可能会使事务管理逻辑变得复杂,因此应该谨慎使用。
  • 在使用JTA事务管理器时,可能需要对数据源进行额外的配置,以确保它们能够与JTA事务管理器协同工作。

@Transactional用于类或方法异同

在Spring框架中,@Transactional 注解可以用于类或方法,它们在事务管理上有不同的作用范围和含义:

类级别的 @Transactional

  • @Transactional 注解应用于整个类时,类中的所有公共方法都会继承这个注解的事务属性。
  • 这意味着,除非在单个方法上指定了不同的事务属性,否则类中所有公共方法都会按照类级别注解定义的事务属性执行。
  • 类级别的事务定义提供了一种方便的方式,当一个类中的多个方法共享相同的事务需求时,可以避免在每个方法上重复使用注解。
@Transactional(readOnly = true)
public class MyService {
    public void methodOne() {
        // ...
    }

    public void methodTwo() {
        // ...
    }
}

在这个例子中,MyService 类的 methodOnemethodTwo 都将以只读事务执行。

方法级别的 @Transactional

  • 方法级别的@Transactional 注解允许你对单个方法进行特定的事务配置,这会覆盖类级别的定义。
  • 这在需要对类中不同方法应用不同事务属性时非常有用。
  • 方法级别的注解提供了更细粒度的事务控制。
public class MyService {
    @Transactional(readOnly = true)
    public void readOnlyMethod() {
        // ...
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void writeMethod() {
        // ...
    }
}

在这个例子中,readOnlyMethod 将作为一个只读事务执行,而 writeMethod 将作为一个全新的写事务执行,即使 MyService 类上有类级别的@Transactional注解。

区别和使用场景:

  • 作用范围:类级别的注解影响该类的所有公共方法,而方法级别的注解只影响单个方法。
  • 事务属性:方法级别的注解可以覆盖类级别的注解定义的事务属性。
  • 使用场景:当你需要为类中的多个方法定义统一的事务行为时,可以使用类级别的注解。当你需要为类中的特定方法定义不同的事务行为时,应使用方法级别的注解。

选择使用类级别还是方法级别的@Transactional注解,应基于你的具体需求和事务管理策略。通常,如果多个方法共享相同的事务配置,类级别的注解会更加方便;而当需要对单个方法进行特殊处理时,方法级别的注解则更加灵活。

总结

在本文中,全面介绍了Spring框架中的@Transactional注解,它是实现声明式事务管理的核心工具。该注解能够自动为方法创建事务代理,通过定义不同的事务属性如传播行为、隔离级别、超时时间等,来精确控制事务的开始、提交或回滚。
特别地,讨论了嵌套事务的概念,它允许在现有事务中创建新的事务,这在处理复杂业务逻辑时非常有用。
此外,我们区分了将@Transactional注解应用于类与方法的不同影响,类级别的注解为所有公共方法提供了统一的事务语义,而方法级别的注解则允许更细粒度的事务控制。最后,我们强调了在使用嵌套事务和@Transactional注解时应注意的一些关键点,包括确保事务管理器和数据库的支持,以及对事务配置的谨慎选择。通过这些深入的讨论,我们希望帮助开发者更好地利用Spring的事务管理功能,以提升应用程序的数据处理能力和事务安全性。