likes
comments
collection
share

大聪明教你学Java | Spring Boot 事务回滚

作者站长头像
站长
· 阅读数 17

前言

我们开发系统的时候经常会遇到一些关于交易的需求,交易的过程大多数都比较繁琐(会包括修改库存、修改余额、记录交易账单等等步骤),这时候我们就不得不考虑其中的潜在风险了,比如我们在交易的过程中修改了库存(库存 -1),接下来需要进行支付操作,但是此时系统突然宕机或者网络突然中断,这也就导致我们无法完成整个交易流程,虽然用户还没付钱,但是我们的库存变少了(商家肯定就不高兴了👿),所以我们就需要用到事务回滚来解决上述的问题。

Spring Boot 事务回滚

我们有两种方式可以实现事务回滚,第一种是自动回滚,第二种是手动回滚,这两种实现方式大同小异,二者都需要使用 @Transactional 注解来实现事务回滚,下面直接上代码,看看二者之间到底哪里不一样。

在接口实现类中有一个插入会员信息的方法,咱们就对这个方法进行改造,分别实现一下自动回滚和手动回滚👇

/**
 * 插入会员信息
 * 
 * @param cashierMember 会员信息
 * @return 结果
 */
@Override
public int insertCashierMember(CashierMember cashierMember)
{
    cashierMember.setCreateTime(DateUtils.getNowDate());
    cashierMember.setCreateBy(ShiroUtils.getLoginName());
    SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
    return cashierMemberMapper.insertCashierMember(cashierMember);
}

自动回滚

/**
 * 插入会员信息
 * 
 * @param cashierMember 会员信息
 * @return 结果
 */
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCashierMember(CashierMember cashierMember)
{
    cashierMember.setCreateTime(DateUtils.getNowDate());
    cashierMember.setCreateBy(ShiroUtils.getLoginName());
    SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
    return cashierMemberMapper.insertCashierMember(cashierMember);
}

我们可以看到方法上增加了一个注解 @Transactional(rollbackFor = Exception.class) ,通过该注解可以对异常进行捕获,当发生异常时就可以进行回滚,从而撤销本次的入库操作。

很多方法中都会用 try-catch 对异常进行处理,如果此时在 catch 中对可能出现的异常进行了处理,但是并没有再手动抛出(throw)异常,Spring 则会认为该方法成功执行,也就不会进行回滚👇。 大聪明教你学Java | Spring Boot 事务回滚 正解如下👇:

/**
 * 插入会员信息
 * 
 * @param cashierMember 会员信息
 * @return 结果
 */
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCashierMember(CashierMember cashierMember)
{
    try {
        cashierMember.setCreateTime(DateUtils.getNowDate());
        cashierMember.setCreateBy(ShiroUtils.getLoginName());
        SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
        return cashierMemberMapper.insertCashierMember(cashierMember);
    }catch (Exception e){
        System.out.println("方法出现异常:" + e);
        //手动抛出异常
        throw new RuntimeException();
    }
}

P.S. 如果 try-catch 语句在 finally 语句块中进行了 return 操作,那么 catch 语句块中手动抛出的异常也会被覆盖,同样不会自动回滚。

手动回滚

手动回滚的实现方式也非常简单,只需要添加一句代码即可实现👇

/**
 * 插入会员信息
 * 
 * @param cashierMember 会员信息
 * @return 结果
 */
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCashierMember(CashierMember cashierMember)
{
    try {
        cashierMember.setCreateTime(DateUtils.getNowDate());
        cashierMember.setCreateBy(ShiroUtils.getLoginName());
        SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
        return cashierMemberMapper.insertCashierMember(cashierMember);
    }catch (Exception e){
        System.out.println("方法出现异常:" + e);
        //实现手动回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
    return 0;
}

P.S. 这里只是举个例子,手动回滚语句不一定要添加在 catch 代码块中,我们可以在任何一个地方使用手动回滚语句。需要注意的是,我们虽然可以在其他地方增加手动回滚语句,但是手动回滚语句后的代码还会继续执行,所以不建议在非 catch 代码块中使用手动回滚语句。 如果非要这么用的话,就一定要好好斟酌一下自己的业务逻辑是不是会有 BUG 了。

Spring Boot 事务回滚注意事项

这里我们再简单说几句关于 Spring Boot 事务回滚中的注意事项:

  1. 想实现回滚,首先要保证 Spring Boot 开启了事务(在启动类上增加 @EnableTransactionManagement 注解开启事务(其实 Spring Boot 默认就是开启事务的),其次就是实现回滚的方法必须是 public 的。
  2. @Transactional(rollbackFor=Exception.class) 表示的是该方法无论抛出什么异常都会进行自动回滚;如果不加 (rollbackFor=Exception.class) 的话,则代表了默认值,也就是只有当该方法抛出了非检查型异常(RuntimeException)时才会进行回滚。
  3. 由于事务的四大特性(原子性、一致性、隔离性、持久性),所以 @Transactional 一般是要加在业务层(也就是接口实现类)中。
  4. 如果将 @Transactional(rollbackFor=Exception.class) 加在了接口实现类上,那么这个类下的所有方法都将会被加上事务管理,即所有方法都会在自己出现异常时进行回滚操作。

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●'◡'●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

爱你所爱 行你所行 听从你心 无问东西