关键词

spring声明式事务 @Transactional 不回滚的多种情况以及解决方案

下面我将详细讲解“spring声明式事务 @Transactional 不回滚的多种情况以及解决方案”。

一、@Transactional不回滚的多种情况

1.1 默认回滚规则

  • 默认情况下,Spring会对所有运行时异常进行回滚。也就是说,只有在方法中抛出RuntimeException及其子类异常时,才会导致事务回滚。
  • 对于受检异常(即继承自Exception的异常),Spring不会自动回滚。

1.2 编程式事务管理

  • 在使用编程式事务管理时,如果没有手动设置任何异常回滚,则不会触发回滚。
  • 如果手动设置回滚条件,但设置的异常类型不是运行时异常,则不会回滚。

1.3 同一个类中的方法调用

  • 由于@Transactional是基于AOP实现的,因此同一个类中的方法调用,不会触发事务回滚。原因是在一个事务方法内部调用另外一个有@Transactional注解的方法,Spring框架并不会再次开启一个新的事务,而是继续使用已经存在的事务。因此即使另一个方法抛出异常,也不会影响当前事务的提交或回滚。

1.4 异常被捕获的情况

  • 如果事务方法抛出一个异常,但是该异常在被当前方法所捕获,而没有被抛到事务方法之外,这时事务并不会进行回滚。这是由于Spring事务管理是通过AOP实现的,只有在方法执行时抛出异常,才会被AOP的拦截器捕获到,才能进行事务回滚。

1.5 事务回滚后的异常被捕获

  • 如果事务方法抛出一个异常导致事务回滚,但是该异常在后续代码中被捕获并处理了,而不是被抛出到上层调用方法,那么回滚不会被成功执行。在处理完异常后,后续代码会继续执行,而不是回滚到之前的状态。这时需要在异常处理中重新抛出异常,在上层调用方法中进行处理。

二、解决方案

2.1 修改默认回滚规则

  • 可以使用@Transactional注解的rollbackFor属性来指定异常回滚的类型,如@Transactional(rollbackFor = Exception.class),这样就可以把受检异常也包含进回滚的规则中,实现更加灵活的回滚控制。

2.2 手动开启新的事务

  • 可以在方法中调用自己所在的类的另一个有@Transactional注解的方法时,通过Propagation.REQUIRES_NEW参数来指定该方法必须开启新的事务,例如:

```java
@Service
public class UserServiceImpl implements UserService {

  @Autowired
  private UserMapper userMapper;

  @Override
  @Transactional
  public void updateUser(User user) {
      userMapper.updateUser(user);
      // 更新用户信息后需要往用户日志表中插入一行新纪录
      this.insertUserLog(user);
  }

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void insertUserLog(User user) {
      UserLog userLog = new UserLog();
      userLog.setUserId(user.getUserId());
      userLog.setOperateType("update");
      userMapper.insertUserLog(userLog);
  }

}
```

2.3 解决异常被捕获的情况

  • 可以将捕获异常的代码改为抛出异常,或者通过TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()来手动设置回滚状态。例如:

java
@Override
@Transactional
public void updateUser(User user) {
try {
userMapper.updateUser(user);
throw new RuntimeException("test");
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw e;
}
}

2.4 解决事务回滚后的异常被捕获

  • 可以通过TransactionAspectSupport.currentTransactionStatus().hasRollbackOnly()方法来判断当前事务是否已经回滚,如果已经回滚,则需要重新抛出异常,例如:

java
@Override
@Transactional
public void updateUser(User user) {
try {
userMapper.updateUser(user);
throw new RuntimeException("test");
} catch (Exception e) {
if (TransactionAspectSupport.currentTransactionStatus().hasRollbackOnly()) {
throw e;
}
// ...处理异常
}
}

好了,这就是关于“spring声明式事务 @Transactional 不回滚的多种情况以及解决方案”的完整攻略。希望对你有所帮助。

本文链接:http://task.lmcjl.com/news/19005.html

展开阅读全文