@Transactional注解的使用

目录

  • 一、@Transactional是什么?
  • 二、原理分析
    • 1.事务的传播行为
    • 2.事务的隔离级别
    • 3.事务的回滚机制
    • 4.@Transactional 注解底层实现
  • 三、使用场景
  • 总结


一、@Transactional是什么?

  • @Transactional 是一个 Spring框架中的注解,用于声明事务的属性。它可以被应用在类或方法上,用于将带有该注解的方法或类中的操作组织成一个原子性的工作单元,要么全部成功提交,要么全部回滚。
  • 在使用 @Transactional注解时,需要将事务的传播行为和隔离级别定义好。传播行为定义了当一个事务方法被另一个事务方法调用时,当前方法应该如何处理事务。隔离级别定义了事务应该如何处理并发访问和修改数据的问题。
  • @Transactional注解可以帮助我们简化事务的处理,避免手动编写事务管理的代码,同时提供了方便的声明式事务处理机制,使得事务的管理变得更加方便和灵活。

二、原理分析

1.事务的传播行为

@Transactional 是Spring框架中的注解,那先来了解一下Spring 事务的传播行为,Spring 事务的传播行为描述了在一个方法内部调用其他带有事务性质的方法时,当前方法与被调用方法之间的事务如何进行传播和交互。

Spring 定义了七种事务传播行为,如下:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前已经存在一个事务,当前方法就在这个事务内运行,否则就创建一个新事务并在其中运行。

  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前已经存在一个事务,当前方法就在这个事务内运行,否则就以非事务的方式运行。

  • TransactionDefinition.PROPAGATION_MANDATORY:当前方法必须在一个已经存在的事务内运行,否则就抛出异常。

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:当前方法必须在一个新的事务内运行,如果当前已经存在一个事务,则挂起该事务。

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:当前方法以非事务方式运行,如果当前已经存在一个事务,则挂起该事务。

  • TransactionDefinition.PROPAGATION_NEVER:当前方法以非事务方式运行,如果当前已经存在一个事务,则抛出异常。

  • TransactionDefinition.PROPAGATION_NESTED:如果当前已经存在一个事务,则在该事务中嵌套一个新的事务,如果当前没有事务,则行为类似于 REQUIRED。在嵌套事务中,内层事务的提交和回滚不会影响到外层事务,但是外层事务的提交会提交内层事务的修改,而内层事务的回滚会回滚外层事务的修改。

2.事务的隔离级别

Spring 事务的隔离级别描述了一个事务在读取和修改数据时的可见性和影响范围。Spring 支持以下五种隔离级别:

  • Isolation.DEFAULT:使用数据库默认的隔离级别。

  • Isolation.READ_UNCOMMITTED:最低的隔离级别,一个事务可以读取另一个事务未提交的数据,可能会导致脏读、幻读和不可重复读等问题。

  • Isolation.READ_COMMITTED:保证一个事务提交后,另一个事务才能读取到其修改的数据,可以避免脏读问题,但可能会出现幻读和不可重复读等问题。

  • Isolation.REPEATABLE_READ:保证一个事务中多次读取同一数据结果一致,可以避免脏读和不可重复读问题,但可能会出现幻读问题。

  • Isolation.SERIALIZABLE:最高的隔离级别,强制事务串行执行,避免了所有并发问题,但性能较差,一般很少使用。

tip:需要注意的是,隔离级别越高,事务的并发性越差,性能也越低。在选择隔离级别时,需要根据具体场景和需求来进行权衡和选择。

3.事务的回滚机制

知道了Spring事务的传播行为和隔离级别,再来了解一下Spring事务的回滚机制。

Spring 事务的回滚机制指的是当事务发生异常时,如何处理已经执行的操作并回滚事务。
例如:当一个事务执行过程中发生异常时,Spring 会根据配置的事务传播机制判断当前事务是否是一个独立的事务,如果是,则会回滚整个事务,回滚操作会撤销当前事务中所有已经执行的操作,将数据恢复到事务开始前的状态。

在 Spring 中,回滚机制是通过 AOP(面向切面编程)实现的,Spring 会将事务相关的代码织入到事务切面中,在事务切面中捕获异常并执行回滚操作(默认是针对 unchecked exception 回滚)。Spring 的事务边界是在调用业务方法之前开始处理,业务方法执行完毕之后来执行 commit or rollback(Spring 默认取决于是否抛出 runtimeException)。

tip:如果在方法中有 try{}catch(Exception e){} 捕获异常,那么 try 里面的代码块就脱离了事务的管理,若要事务生效需要在 catch 中 throw new RuntimeException ("xxxxxx");

4.@Transactional 注解底层实现

@Transactional注解的底层实现是通过AOP(面向切面编程)来实现的。在Spring框架中,使用了TransactionInterceptor拦截器来实现@Transactional注解的功能。TransactionInterceptor是一个AOP拦截器,它在目标方法执行之前和之后分别开启和提交事务。
具体来说,当方法上标记了@Transactional注解时,Spring会在运行时生成一个代理对象,该代理对象会拦截对该方法的调用,并在执行方法之前开启一个事务,然后在执行完成后根据方法的执行结果来决定是提交事务还是回滚事务。

在底层实现中,TransactionInterceptor使用了TransactionManager来管理事务,而TransactionManager则使用了底层的数据源来实现事务的管理。在事务提交或回滚时,TransactionInterceptor会根据事务的状态来决定是否提交或回滚,同时还会负责管理事务的传播行为和隔离级别等事务属性。

需要注意,在同一个类中调用当前类其他的方法@Transactional注解的功能是不生效!

@Transactional注解的使用_第1张图片
在类中调用其他方法是调用者直接调用目标类。

想要@Transactional注解生效,可以通过 AopContext.currentProxy () 获取到本类的代理对象,再去调用就可以了。因为这种方式是 CGLIB 实现,所以要开启 AOP,在 springboot 启动类上加上注解 @EnableAspectJAutoProxy(exposeProxy = true)

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.util.TimeZone;

/**
 * zhoqua应用程序
 *
 * @author zhoqua
 * @version 1.0.0
 * @since 2023/04/26
 */
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class ZhoquaApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZhoquaApplication.class, args);
        // 设置时区
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
    }

}
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    @Transactional(rollbackFor = Exception.class)
    public User updateUser(User user) {
        UserServiceImpl currentProxy= (UserServiceImpl) AopContext.currentProxy();
        currentProxy.updatePhone(user.getPhone());
        
        return user;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updatePhone(String phone) {
        // TODO 此处省略1w行
        return null;
    }
}

tip:要注意,代理对象调用的方法必须 public 修饰符,否则方法中获取不到注入的 bean,会报空指针错误。


三、使用场景

下面列举了一些使用场景:

数据库事务: 在处理数据库事务时,应该使用 @Transactional 注解来确保所有的操作都能够成功提交或回滚,从而保证数据的一致性和完整性。例如,当一个方法中需要对多个数据库表进行更新操作时,应该使用 @Transactional 注解来保证这些操作在同一个事务中执行。

业务逻辑: 在处理某些业务逻辑时,需要确保多个操作的原子性,例如,当用户下单时,需要同时更新用户的账户信息和订单信息。使用 @Transactional 注解可以确保这些操作在同一个事务中执行。

缓存更新: 在更新缓存时,需要确保缓存和数据库的一致性。使用 @Transactional 注解可以确保在更新数据库之前,缓存已经被更新或者被清空。

消息队列: 在使用消息队列处理业务时,需要确保消息被正确处理或者回滚。使用 @Transactional 注解可以保证在处理消息时,消息和数据库的操作是在同一个事务中执行的。


总结

@Transactional注解的底层实现是基于AOP和TransactionInterceptor拦截器来实现的,它使用了TransactionManager来管理事务,确保了在一个事务内执行的所有操作都是原子性、一致性、隔离性和持久性的。@Transactional 注解可以确保一个方法或类中的操作要么全部成功提交,要么全部回滚,从而保证数据的完整性和一致性。它适用于许多场景,包括数据库事务、业务逻辑、缓存更新、消息队列等。

你可能感兴趣的:(java,SpringBoot,java,spring,boot)