Spring事务传播行为

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 项目,目录如下

项目结构.png

我们主要使用 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失败失败
requiredrequired失败失败
requiredrequired_new失败失败
requirednested失败失败
requiredmandatory失败失败
requiredsupports失败失败
requirednot_supports失败成功
requirednever失败失败

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

  1. 调用方法使用 SUPPORTS
调用方法被调用方法调用方法执行结果被调用方法执行结果
supports成功成功
supportsrequired成功失败
supportsrequired_new成功失败
supportsnested成功失败
supportsmandatory失败失败
supportssupports成功成功
supportsnot_supports成功成功
supportsnever成功成功

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

  1. 调用方法使用 MANDATORY
调用方法被调用方法调用方法执行结果被调用方法执行结果
mandatory失败失败
mandatoryrequired失败失败
mandatoryrequired_new失败失败
mandatorynested失败失败
mandatorymandatory失败失败
mandatorysupports失败失败
mandatorynot_supports失败失败
mandatorynever失败失败

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

  1. 调用方法使用 NOT_SUPPORTS
调用方法被调用方法调用方法执行结果被调用方法执行结果
not_supports成功成功
not_supportsrequired成功失败
not_supportsrequired_new成功失败
not_supportsnested成功失败
not_supportsmandatory成功失败
not_supportssupports成功成功
not_supportsnot_supports成功成功
not_supportsnever成功成功

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

  1. 调用方法使用 REQUIRES_NEW
调用方法被调用方法调用方法执行结果被调用方法执行结果
requires_new失败失败
requires_newrequired失败失败
requires_newrequired_new失败失败
requires_newnested失败失败
requires_newmandatory失败失败
requires_newsupports失败失败
requires_newnot_supports失败成功
requires_newnever失败失败

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

  1. 调用方法使用NEVER
调用方法被调用方法调用方法执行结果被调用方法执行结果
never成功成功
neverrequired成功失败
neverrequired_new成功失败
nevernested成功失败
nevermandatory成功失败
neversupports成功成功
nevernot_supports成功成功
nevernever成功成功

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

  1. 调用方法使用NESTED
调用方法被调用方法调用方法执行结果被调用方法执行结果
nested失败失败
nestedrequired失败失败
nestedrequired_new失败失败
nestednested失败失败
nestedmandatory失败失败
nestedsupports失败失败
nestednot_supports失败成功
nestednever失败失败

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

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

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

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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值