桃李不言,下自成蹊。

桃树李树虽不会说话,但是它们果实甜美,惹人喜爱,人们在它下面走来走去,走成了一条小路。


前言

spring事务传播行为的含义:

简单的理解就是多个事务方法相互调用时,事务如何在这些方法间传播。
默认是REQUIRED。

一、7种事务传播类型

7种事务传播类型分别是:
REQUIRED、SUPPORTS、MANDATORY
REQUIRES_NEW、NOT_SUPPORTED、NEVER
NESTED

这种东西没必要刻意去记,死记硬背过后也是会遗忘的,我们要真正理解其含义。
spring事务传播机制可以分为三组

1.1 支持当前事务

支持当前事务的传播机制有三种,分别是
1 REQUIRED (必须有)

含义:如果当前方法没有事务,新建一个事务,如果已经存在一个事务中,则加入到这个事务中。

2 SUPPORTS (可有可无)

含义:支持当前事务,如果当前没有事务,就以非事务方式执行

3 MANDATORY (强制)

含义:使用当前的事务,如果当前没有事务,就抛出异常。

1.2 不支持当前事务

4 REQUIRES_NEW

含义:新建事务,如果当前存在事务,把当前事务挂起。

5 NOT_SUPPORTED

含义:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6 NEVER

含义: 以非事务方式执行,如果当前存在事务,则抛出异常。

1.3 NESTED

含义: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

二、示例

案例场景:每次调用创建用户接口的同时,都要记录一下该操作日志;创建用户方法会向user表中插入一条数据,操作日志方法会向log表中插入一条数据。

2.1 saveUser 配置事务 required, saveLog 无事务

@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserRepository userRepository;

  @Autowired
  private LogService logService;
  
  @Override
  @Transactional(propagation = Propagation.REQUIRED)
  public void saveUser() {
    User user = new User();
    user.setName("dly");

    userRepository.save(user);

    saveLog();

  }

  @Override
  public void saveLog() {
    Log log = new Log();
    log.setContent("新增用户");

    logService.saveLog(log);
   
    // 异常 / by zero
    int i = 1 / 0;
  }
 }

结论:在这个案例中user、log都不会存储成功。
原因: 1. addUser 方法中配置事务为required
2. saveLog未配置事务,但是addUser调用了saveLog,由于事务传播类型为requried,所以saveLog也是有事务的, 所以出现异常时,触发回滚,哪个也存不进去。

2.2 saveUser 无事务, saveLog事务为required

@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserRepository userRepository;

  @Autowired
  private LogService logService;

  @Autowired
  private UserService userService;

  @Override
  public void saveUser() {
    User user = new User();
    user.setName("dly");

    userRepository.save(user);

    userService.saveLog();

  }

  @Override
  @Transactional(propagation = Propagation.REQUIRED)
  public void saveLog() {
    Log log = new Log();
    log.setContent("新增用户");

    logService.saveLog(log);

    // 异常
    int i = 1 / 0;
  }

结论:user插入成功,log失败
原因:1.saveUser方法没有事务
2.saveUser调用了saveLog,saveLog方法的事务只作用在当前方法上面,因此发生异常时,log回滚,user没有事务,不回滚。

补充:因为spring 中事务的实现使用的是AOP,也就是通过动态代理实现的,因此,在案例中需要注入Service类型,借助通过spring代理的service对象来完成同一个类中方法的相互调用,不能使用this.方法名(),直接this调用是没有事务的。

2.3 saveUser 配置事务required,saveLog配置事务not_supported

@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserRepository userRepository;

  @Autowired
  private LogService logService;

  @Autowired
  private UserService userService;

  @Override
  @Transactional(propagation = Propagation.REQUIRED)
  public void saveUser() {
    User user = new User();
    user.setName("dly");

    userRepository.save(user);

    userService.saveLog();

  }

  @Override
  @Transactional(propagation = Propagation.NOT_SUPPORTED)
  public void saveLog() {
    Log log = new Log();
    log.setContent("新增用户");

    logService.saveLog(log);

    // 异常
    int i = 1 / 0;
  }


结论:user 插入失败,log插入成功
原因:1.saveUser 事务是required
2.saveLog事务是not_supported,意思是以非事务方式运行,如果存在事务,则把事务挂起,因此可以理解为saveLog方法是没有事务的,所以抛处异常后,user回滚,log不回滚。

not_supported,以非事务方式运行在记录日志这种场景时是很合适的,就日志而言,不管你成功与否,我都要记录。

2.4 saveUser事务required,saveLog配置事务never

@Service
public class UserServiceImpl implements UserService {
  @Autowired
  private UserRepository userRepository;

  @Autowired
  private LogService logService;

  @Autowired
  private UserService userService;

  @Override
  @Transactional(propagation = Propagation.REQUIRED)
  public void saveUser() {
    User user = new User();
    user.setName("dly");

    userRepository.save(user);

    userService.saveLog();

  }

  @Override
  @Transactional(propagation = Propagation.NEVER)
  public void saveLog() {
    Log log = new Log();
    log.setContent("新增用户");

    logService.saveLog(log);

  }

结论:user、log 都插入失败
原因:1.saveLog中配置的事务机制为never,表示如果存在事务,则抛处异常,因此user、log全部回滚。
抛出的异常为:
IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’

总结

spring的事务传播默认使用的是required, 也是最常用的,而且spring的事务处理是使用aop动态代理来实现的,如果使用不当就会出现spring事务失效的问题。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐