Spring事务7种传播机制(代码示例)

Spring事务7种传播机制(代码示例)

文章目录

  • Spring事务7种传播机制(代码示例)
    • 前言
    • 1.项目环境
    • 2.相关资料
      • 2.1 Spring 源码
      • 2.2 官网地址
    • 3.示例代码
      • 3.1 无事务示例
        • 3.1.1 正常示例
        • 3.1.2 异常示例
      • 3.2 REQUIRED(默认)
        • 3.2.1 代码示例
        • 3.2.2 结果分析
      • 3.3 REQUIRES_NEW
        • 3.3.1 代码示例
        • 3.3.2 结果分析
      • 3.4 SUPPORTS
        • 3.4.1 代码示例
        • 3.4.2 结果分析
      • 3.5 MANDATORY
        • 3.5.1 代码示例
        • 3.5.2 结果分析
      • 3.6 NOT_SUPPORTED
        • 3.6.1 代码示例
        • 3.6.2 结果分析
      • 3.7 NEVER
        • 3.7.1 代码示例
        • 3.7.2 结果分析
      • 3.8 NESTED
        • 3.8.2 结果分析
    • 4.NESTED 与 REQUIRED 区别

前言

本章主要讨论 Spring Transaction 事务的 7 种传播机制,看了网上大部分文章都是一些概念,看完之后似懂非懂,为了真正的了解这几种传播机制,本文中会对每种传播机制有实际的代码进行演示和总结,以此来加深理解。

1.项目环境

  • jdk 1.8
  • spring 5.2.8.RELEASE
  • github 地址:https://github.com/huajiexiewenfeng/spring-transaction-demo

2.相关资料

2.1 Spring 源码

其实 Spring 源码中就有详细的解释,代码位置:

  • org.springframework.transaction.annotation.Propagation

源码如下(省略了注释,注释中会对每种方式做简要的说明,下面文章我们会进行讨论):

public enum Propagation {
    REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),	    	                	 
    SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
	NEVER(TransactionDefinition.PROPAGATION_NEVER),
	NESTED(TransactionDefinition.PROPAGATION_NESTED);

	private final int value;
	Propagation(int value) {
		this.value = value;
	}
	public int value() {
		return this.value;
	}
}

2.2 官网地址

https://docs.spring.io/spring-framework/docs/5.2.8.RELEASE/spring-framework-reference/data-access.html#tx-propagation

官网只对 REQUIRED、REQUIRES_NEW、NESTED 三种做了说明。

3.示例代码

以下所有示例均按照 caller -> method-1->method-2 的方式来进行演示,与官网保持一致

Spring事务7种传播机制(代码示例)_第1张图片

3.1 无事务示例

3.1.1 正常示例

注意:@Transactional 限制

  • 注解方法必须为 public;
  • 调用时使用 IoC 注入获取的 Spring Bean 对象进行调用,才会触发 AOP 切面效果。

我们先写一个非事务版本,代码如下:

UserController

@RestController
public class UserController {

  @Autowired
  private UserService userService;

  @GetMapping("addUser")
  public String addUser() {
    // method-1
    userService.addUser(new User("小明", 18));
    return "success";
  }
}

UserImpl

@Service
public class UserImpl implements UserService {

  @Autowired
  private UserMapper userMapper;

  @Override
  public void addUser(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUser(user);
  }
}

User2Impl

@Service
public class User2Impl implements User2Service {

  @Autowired
  private UserMapper userMapper;

  @Override
  public void addUser(User user) {
    User newUser = new User(user.getName() + "-new", 18);
    userMapper.addUser(newUser);
  }
}

浏览器输入:http://127.0.0.1:8185/addUser

执行结果如下:

Spring事务7种传播机制(代码示例)_第2张图片

两条数据正常插入。

3.1.2 异常示例

UserController

  @GetMapping("addUserException")
  public String addUserException() throws Throwable {
    // method1
    userService.addUserException(new User("小明", 18));
    return "success";
  }

UserImpl

  @Override
  public void addUserException(User user) throws Exception {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserException(user);
  }

User2Impl

  @Override
  public void addUserException(User user) throws Exception {
    User newUser = new User(user.getName() + "-exception", 18);
    int i = 1 / 0;
    userMapper.addUser(newUser);
  }

浏览器输入:http://127.0.0.1:8185/addUserException

执行结果:

Spring事务7种传播机制(代码示例)_第3张图片

只有第一条插入成功。

3.2 REQUIRED(默认)

  • Support a current transaction, create a new one if none exists.

  • This is the default setting of a transaction annotation.

支持当前事务,如果当前事务不存在,创建一个新的事务。事务注解 @Transactional 的默认设置。

Spring事务7种传播机制(代码示例)_第4张图片

从上图中可以看到,method 1 和 method 2 是用的同一个 Transaction 事务。

3.2.1 代码示例

UserController

  @GetMapping("addUserTransactional/required")
  public String addUserTransactionalRequired() throws Exception {
    // method-1
    userService.addUserTransactionalRequired(new User("小明required", 18));
    return "success";
  }

UserImpl

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserTransactionalRequired(User user) throws Exception {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalException(user);
  }

User2Impl

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserTransactionalException(User user) throws Exception {
    User newUser = new User(user.getName() + "-exception", 18);
    userMapper.addUser(newUser);
    throw new RuntimeException("异常测试");
  }

浏览器输入:http://127.0.0.1:8185/addUserTransactional/required

执行结果:数据库无新增数据

method-2 抛出异常,method-1 和 method-2 事务一起回滚。

同样的我们可以把异常在 method-1中抛出

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUser1TransactionalRequired(User user) throws Exception {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalException(user);
    throw new RuntimeException("异常测试");
  }

执行结果:数据库无新增数据

method-1 抛出异常,method-1 和 method-2 事务一起回滚。

3.2.2 结果分析

method-1(有事务)调用 method-2(有事务)

  • 如果 method-1 抛出异常,method-1 和 method-2 事务一起回滚
  • 如果 method-2 抛出异常,method-1 和 method-2 事务一起回滚

REQUIRED 这种默认的传播机制比较常用,符合我们一般的业务场景,多个操作要么一起成功,要么一起失败,保证业务操作的一致性。

3.3 REQUIRES_NEW

Create a new transaction, and suspend the current transaction if one exists.

创建一个新的事务,如果当前事务存在,则挂起当前事务。

Spring事务7种传播机制(代码示例)_第5张图片

从上图中可以看到,每一个方法都会创建一个新的 Transaction 事务。由此可以猜到,method-1 如果抛出异常,method-1 的事务会进行回滚,但是并不会影响 method-2,下面我们用代码来进行验证。

PS:测试结果与猜想的有点小差别。

3.3.1 代码示例

UserController

  @GetMapping("addUserTransactional/requires_new")
  public void addUserTransactionalRequiresNew() throws Exception {
    // method-1
    userService.addUserTransactionalRequiresNew(new User("小明requires_new", 18));
  }

UserImpl

  @Override
  public void addUserTransactionalRequiresNew(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalRequiresNewException(user);
  }

User2Impl

  @Override
  @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
  public void addUserTransactionalRequiresNewException(User user) throws Exception {
    User newUser = new User(user.getName() + "-exception", 18);
    userMapper.addUser(newUser);
    throw new RuntimeException("异常测试");
  }

浏览器输入:http://127.0.0.1:8185/addUserTransactional/requires_new

执行结果:数据库无新增数据

method-2 抛出异常,method-2先回滚, method-1 接收到 method-2 的异常,也进行回滚。

我们把异常移动到 method-1 中

UserImpl

  @Override
  @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
  public void addUser1TransactionalRequiresNew(User user) throws Exception {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalRequiresNewException(user);
    throw new RuntimeException("异常测试");
  }

执行结果:

Spring事务7种传播机制(代码示例)_第6张图片

method-2 执行成功,插入了记录;method-1 出现异常,进行回滚,但是并不影响 method-2。

3.3.2 结果分析

method-1(有事务)调用 method-2(有事务)

  • 如果 method-1 抛出异常,method-1 事务回滚,而 method-2 不受外部异常影响,成功执行插入
  • 如果 method-2 抛出异常,method-1 和 method-2 事务一起回滚

3.4 SUPPORTS

Support a current transaction, execute non-transactionally if none exists.

支持当前事务,如果当前事务不存在,则以非事务方式执行。

3.4.1 代码示例

userController

  @GetMapping("addUserTransactional/supports")
  public void addUserTransactionalSupports() throws Exception {
    // method-1
    userService.addUserSupports(new User("小明supports", 18));
  }

UserImpl

  @Override
  public void addUserSupports(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalSupportsException(user);
  }

User2Impl

  @Override
  @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
  public void addUserTransactionalSupportsException(User user) {
    User newUser = new User(user.getName() + "-exception", 18);
    userMapper.addUser(newUser);
    throw new RuntimeException("异常测试");
  }

http://127.0.0.1:8185/addUserTransactional/supports

执行结果:

Spring事务7种传播机制(代码示例)_第7张图片

method-1 执行成功,插入了记录;method-2 出现异常,但是也插入成功了,因为 method-1 不存在事务,所以 method-2 以非事务方式执行。

如果我们把 method-1 中增加事务

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserSupports(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalSupportsException(user);
  }

再次执行,数据库无新增数据,表示 method-2 异常,method-1 和 method-2 一起回滚。

3.4.2 结果分析

method-1(无事务)调用 method-2(有事务)

  • 如果 method-2 抛出异常,method-1 和 method-2 都不会回滚;

method-1(有事务)调用 method-2(有事务)

  • 如果 method-2 抛出异常,method-1 和 method-2 一起回滚。

3.5 MANDATORY

Support a current transaction, throw an exception if none exists.

支持当前事务,如果不存在事务,则抛出异常。

3.5.1 代码示例

UserController

  @GetMapping("addUserTransactional/mandatory")
  public void addUserTransactionalMandatory() throws Exception {
    // method-1
    userService.addUserMandatory(new User("小明mandatory", 18));
  }

UserImpl

  @Override
  public void addUserMandatory(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserMandatory(user);
  }

User2Impl

  @Override
  @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
  public void addUserMandatory(User user) {
    User newUser = new User(user.getName() + "-new", 18);
    userMapper.addUser(newUser);
  }

执行结果:

抛出异常

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

3.5.2 结果分析

method-1(无事务) 调用 method-2(有事务)

  • method-2 抛出异常 No existing transaction found for transaction marked with propagation 'mandatory'

method-1(有事务) 调用 method-2(有事务)

  • method-2 参照默认事务的方式执行,要么一起回滚,要么一起成功。

3.6 NOT_SUPPORTED

Execute non-transactionally, suspend the current transaction if one exists.

以非事务方式执行,如果存在当前事务,则挂起当前事务。

3.6.1 代码示例

UserController

  @GetMapping("addUserTransactional/not_supported")
  public void addUserTransactionalNotSupported() throws Exception {
    // method-1
    userService.addUserNotSupports(new User("小明not_supported", 18));
  }

UserImpl

  @Override
  public void addUserNotSupports(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalNotSupportsException(user);
  }

User2Impl

  @Override
  @Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
  public void addUserTransactionalNotSupportsException(User user) {
    User newUser = new User(user.getName() + "-exception", 18);
    userMapper.addUser(newUser);
    throw new RuntimeException("异常测试");
  }

执行结果:

Spring事务7种传播机制(代码示例)_第8张图片

method-2 抛出异常,但是插入成功,因为以非事务的方式执行,method-1 本来就没有事务,所以也执行成功。

如果我们给 method-1 增加事务

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserNotSupports(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalNotSupportsException(user);
  }

执行结果:

Spring事务7种传播机制(代码示例)_第9张图片

method-2 抛出异常,但是插入成功,因为以非事务的方式执行,但是 method-1 有事务,获取到 method 抛出的异常,进行回滚。

3.6.2 结果分析

method-1(无事务) 调用 method-2(有事务)

  • method-2 抛出异常,method-1 不会回滚,method-2 以非事务方式执行,也不会回滚,插入成功;

method-1(有事务) 调用 method-2(有事务)

  • method-1 抛出异常,进行回滚;而 method-2 不会回滚,method-2 以非事务方式执行;
  • method-2 抛出异常,但不会回滚;而 method-1 获取异常进行回滚。

3.7 NEVER

Execute non-transactionally, throw an exception if a transaction exists.

以非事务方式执行,如果存在事务则抛出异常。

3.7.1 代码示例

UserController

  @GetMapping("addUserTransactional/never")
  public void addUserTransactionalNever() throws Exception {
    // method-1
    userService.addUserNever(new User("小明never", 18));
  }

UserImpl

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserNever(User user) {
    userMapper.addUser(user);
    // method-2
    user2Service.addUserTransactionalNever(user);
  }

User2Impl

  @Override
  @Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class)
  public void addUserTransactionalNever(User user) {
    User newUser = new User(user.getName() + "-new", 18);
    userMapper.addUser(newUser);
  }

执行结果:

抛出异常

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

3.7.2 结果分析

method-1(有事务) 调用 method-2(有事务)

  • method-2 抛出异常 Existing transaction found for transaction marked with propagation 'never'
    • 这一点 NEVER 与 MANDATORY 正好相反

method-1(无事务) 调用 method-2(有事务)

  • method-1 和 method-2 都按照无事务的方式执行。

3.8 NESTED

Execute within a nested transaction if a current transaction exists,

behave like {@code REQUIRED} otherwise.

如果当前事务存在,则在嵌套事务中执行,否则行为类似 REQUIRED。

UserController

  @GetMapping("addUserTransactional/nested")
  public void addUserTransactionalNested() throws Exception {
    // method-1
    userService.addUserNested(new User("小明nested", 18));
  }

UserImpl

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserNested(User user) {
    userMapper.addUser(user);
    user2Service.addUserTransactionalNested(user);
    throw new RuntimeException("异常测试");
  }

User2Impl

  @Override
  @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
  public void addUserTransactionalNested(User user) {
    User newUser = new User(user.getName() + "-exception", 18);
    userMapper.addUser(newUser);
//    throw new RuntimeException("异常测试");
  }

执行结果:数据库无新增数据

不论异常是在 method-1 或者 method-2 中抛出,都会一起回滚。

3.8.2 结果分析

method-1(有事务) 调用 method-2(有事务)

  • method-2 抛出异常,method-1 和 method-2 一起回滚;
  • method-1 抛出异常,method-1 和 method-2 一起回滚;

method-1(无事务) 调用 method-2(有事务)

  • method-1 按照无事务的方式执行,method-2 按照默认事务方式执行。

4.NESTED 与 REQUIRED 区别

从两者的示例结果分析来看,这两个没有区别;

我们把上面的两个例子再改造一下,给 method-2 增加 try catch 语句来捕捉异常

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserTransactionalRequired(User user) throws Exception {
    userMapper.addUser(user);
    // method-2
    try {
      user2Service.addUserTransactionalException(user);
    }catch (Exception e){
      e.printStackTrace();
    }
  }

  @Override
  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
  public void addUserNested(User user) {
    userMapper.addUser(user);
    try{
    user2Service.addUserTransactionalNested(user);
    }catch (Exception e){
      e.printStackTrace();
    }
  }

执行 REQUIRED 结果:

抛出异常

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

执行 NESTED 结果:

Spring事务7种传播机制(代码示例)_第10张图片

method-1 执行成功,method-2 回滚。

总结一下差别:

  • 在 REQUIRED 中,method-1 和 method-2 是同一个事务;但是在 NESTED 中 ,method-2 是 method-1 的子事务(嵌套事务)
  • 在 REQUIRED 中,method-2 抛出异常不能被 try catch 捕获,否则会抛出 UnexpectedRollbackException 异常;但是在 NESTED 中,method-2 的异常可以被 try catch 捕获。

你可能感兴趣的:(spring,java,后端)