欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring事务传播行为

程序员文章站 2022-05-06 21:37:53
...

Spring 提供了对数据库事务的支持,除了常说的事务隔离级别,Spring 定义了不同的事务传播行为,用来简化我们在应用代码中事务在不同的方法中的传播,在平时的开发中我们可以简单地说,Spring 的事务传播就是解决被调用方法如果出错那么调用方是否进行回滚的问题。但是长久以来,我都不是很理解这些传播行为究竟具体表示什么意思,即使看过很多讲述的文章,但是看过之后还是不清楚各种传播行为配合使用的结果是怎样的,所以本文谨以实验的方式来记录一下各种传播行为所产生的结果,以加深对其使用的理解。
本文使用 Springboot + MyBatis,仅通过示例代码演示各种事务传播行为时,方法的执行情况。

本文只试验被调用方法中出现异常并且未被捕获时的事务传播情况,调用方法中出现异常的情况下次再进行试验说明

1. 创建表

# user 表
 create table if not exists user (
	id bigint	not null auto_increment,
	name varchar(20) not null,
	sex tinyint,
	create_time datetime not null,
	primary key (id)
) engine=INNODB default charset=utf8mb4;

# 账户表
create table if not exists account(
	id bigint not null auto_increment,
	balance decimal(14, 2) not null default 0.00,
	user_id bigint not null,
	create_time datetime not null,
	primary key (id)
) engine = INNODB default charset=utf8mb4;

2. 搭建Springboot 项目,目录如下

Spring事务传播行为

我们主要使用 UserService.java 和 AccountService.java 来演示,代码如下:

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void add(Account account) {
        accountMapper.insert(account);
        int i = 1 / 0;
    }
}

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AccountService accountService;

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void add(User user, Account account) {
        userMapper.insertUser(user);
        accountService.add(account);
    }
}

可以看到,我们在UserService.add() 方法中调用了 AccountService.add() 方法,并且我们在AccountService.add() 方法中制造一个异常,两个方法的作用都是向对应的表中插入一条数据,下面我们使用不同的传播行为来进行测试,数据的关联正确性请忽略。

  1. 调用方法使用 REQUIRED
调用方法 被调用方法 调用方法执行结果 被调用方法执行结果
required 失败 失败
required required 失败 失败
required required_new 失败 失败
required nested 失败 失败
required mandatory 失败 失败
required supports 失败 失败
required not_supports 失败 成功
required never 失败 失败

required: 支持当前事务,如果被调用方法没有事务,则将其加入到调用方的事务中;如果被调用方法是not_supported,则将其以非事务方式执行,即被调用方法不会滚;如果被调用方法标记了 never, 则表示调用方不能存在事务,否则会抛出异常,所以被调用方法不是由于 1/0 而回滚,而是因为事务冲突的原因没有得到执行而直接抛出了异常。

  1. 调用方法使用 SUPPORTS
调用方法 被调用方法 调用方法执行结果 被调用方法执行结果
supports 成功 成功
supports required 成功 失败
supports required_new 成功 失败
supports nested 成功 失败
supports mandatory 失败 失败
supports supports 成功 成功
supports not_supports 成功 成功
supports never 成功 成功

supports: 支持当前事务,如果没有事务,则以非事务方式运行。如果被调用方法有非强制的事务(required, requires_new),则被调用方法会回滚,调用方法不会回滚;如果被调用方法有强制事务(mandatory), 则调用方法会抛出事务冲突的的异常,被调用方法得不到执行,所以调用方法和被调用方法都会回滚;如果被调用方没有事务,则调用方法和被调用方法都已非事务方式运行,不会进行回滚。

  1. 调用方法使用 MANDATORY
调用方法 被调用方法 调用方法执行结果 被调用方法执行结果
mandatory 失败 失败
mandatory required 失败 失败
mandatory required_new 失败 失败
mandatory nested 失败 失败
mandatory mandatory 失败 失败
mandatory supports 失败 失败
mandatory not_supports 失败 失败
mandatory never 失败 失败

mandatory: 支持当前事务,如果没有事务,则抛出异常。当被调用方法标记了not_supported、 never或者没有事务时,直接抛出事务异常,被调用方法不能得到执行,所以失败;其他情况均以事务方式执行,所以进行了回滚。

  1. 调用方法使用 NOT_SUPPORTS
调用方法 被调用方法 调用方法执行结果 被调用方法执行结果
not_supports 成功 成功
not_supports required 成功 失败
not_supports required_new 成功 失败
not_supports nested 成功 失败
not_supports mandatory 成功 失败
not_supports supports 成功 成功
not_supports not_supports 成功 成功
not_supports never 成功 成功

not_supported: 不支持当前事务,如果当前有事务,则将其挂起,以非事务方式执行。在调用方法上标记not_supported, 则调用方法不会进行回滚,被调用方法是否回滚取决于被调用方法自身的事务机制。

  1. 调用方法使用 REQUIRES_NEW
调用方法 被调用方法 调用方法执行结果 被调用方法执行结果
requires_new 失败 失败
requires_new required 失败 失败
requires_new required_new 失败 失败
requires_new nested 失败 失败
requires_new mandatory 失败 失败
requires_new supports 失败 失败
requires_new not_supports 失败 成功
requires_new never 失败 失败

requires_new: 不支持当前事务,如果没有事务,则新建事务,如果有事务,则将当前事务挂起。如果被调用方法上标记了never,则直接抛出事务异常,被调用方法不能得到执行,所以调用方法也进行回滚; 如果被调用方法标记了not_supported, 则被调用方法以非事务方式执行,不会进行回滚,调用方法由于1/0的异常而进行回滚;如果被调用方法没有事务,则新建事务,所以进行了回滚。

  1. 调用方法使用NEVER
调用方法 被调用方法 调用方法执行结果 被调用方法执行结果
never 成功 成功
never required 成功 失败
never required_new 成功 失败
never nested 成功 失败
never mandatory 成功 失败
never supports 成功 成功
never not_supports 成功 成功
never never 成功 成功

never: 不支持当前事务,以非事务方式运行,如果当前有事务,则抛出异常。所以,当被调用方有强制事务时,直接抛出事务冲突的异常导致没有得到执行导致失败;当被调用方法有required、requires_new时,因为 1/0而进行回滚。但是如果never是被标记在被调用方式上时,如果调用方法有事务就会抛出异常。

  1. 调用方法使用NESTED
调用方法 被调用方法 调用方法执行结果 被调用方法执行结果
nested 失败 失败
nested required 失败 失败
nested required_new 失败 失败
nested nested 失败 失败
nested mandatory 失败 失败
nested supports 失败 失败
nested not_supports 失败 成功
nested never 失败 失败

nested: 如果存在事务,则以嵌套事务方式执行。可以看到,在不捕获异常的情况下,和required的方式相似,如果被调用方法标记了 nested, 并且在调用方法中try-catch了,则只有当调用方法提交后,被调用方法才会被提交,否则被调用方法会回滚。

3. 总结

  • 什么是当前事务?
    相对于被调用方法来说,调用方法的事务状态就是当前事务。

  • 什么是支持当前事务?
    被调用方法是否按照当前事务的执行状态来运行,如果是,则表示支持当前事务。

可以看到,Spring中事务传播行为主要分为两种类型:支持当前事务(required/ mandatory/ support)、 不支持当前事务(required_new/ not_supported/ never)。

以上谨代表个人的理解,如有不当之处,敬请批评指正。

相关标签: Spring源码学习