事务没有回滚?@Transactional你用对了嘛!

1,260 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

最近项目中写业务的时候遇到了小问题,在操作多个表时,多个业务处理方法使用了@Transactional开启事务,但是在测试的时候发现执行失败时事务并没有回滚,这让对@Transactional事务一知半解的我大受震撼,便准备一探究竟,顺便学上一手。

1. 问题再现

1.1 业务代码信息

业务实体类信息:

@Data
public class ProductCodeEntity {
   /**
    * Id
    */
   private Long productCodeId;
   /**
    * 子类集合
    */
   private List<CodeMatnrRelatedEntity> productMatrialList;
    ...
}

对上层暴露的业务方法:

//控制层调用的业务方法
public void save(ProductCodeEntity entity) {
    //一些其他逻辑的处理
    ...
    saveProductCodeInfo(entity);
}

主表业务处理逻辑:

//主表保存
@Transactional(rollbackFor = Exception.class)
public void saveProductCodeInfo(ProductCodeEntity entity) {
    productCodeMapper.save(entity);
    if(CollectionUtils.isNotEmpty(entity.getProductMatrialList())){
        entity.getProductMatrialList().stream().forEach(a -> a.setProductCodeId(entity.getProductCodeId()));
        codeMatnrRelatedService.batchSave(entity.getProductMatrialList());
    }
}

子表业务处理逻辑:

//子表批量保存
@Transactional(rollbackFor = Exception.class)
public int batchSave(List<CodeMatnrRelatedEntity> entityList) {
    return codeMatnrRelatedMapper.batchSave(entityList);
}

1.2 业务功能描述

  • 在进行业层逻辑处理时,由于涉及到一些其他数据的计算,抽取了单独的方法,并在对外暴露的业务方法中调用,之后再调用保存方法并传入参数。
  • 主表数据保存方法中调用子表信息的批量保存,在数据正常时两表数据均保存成功;但是当主表保存成功后子表数据出现了异常,此时@Transactional注解并没有将事务进行回滚。

2. @Transaction注解

2.1 @Transaction是什么

  • @Transaction注解是Spring框架提供的一种声明式事务,建立在Spring的AOP至上。声明式事务的本质是在方法的前后进行拦截,在目标方法开始时进行事务创建或者加入一个已经创建的事务,而在方法执行完成后对事务进行提交或者回滚。
  • 在SpringBoot中使用@Transaction注解标注在方法之上,便对当前方法开启了事务。
  • @Transaction注解可以应用在接口、接口方法、类以及类方法上。

2.2 @Transaction的使用

  1. 在项目的启动类上添加@EnableTransactionManagement注解
  2. 注解加在类上则类中所有方法都会应用到,如果加在方法仅当前方法应用到
  3. @Transaction注解可以配置回滚异常的范围,默认是RuntimeException事务回滚,可以使用@Transaction(rollbackFor=Exception.class)来设置所有异常时事务回滚
  4. 使用@Transaction注解后,当遇到符合范围的异常时,事务回滚,数据库数据也会回滚。

2.3 @Transaction的失效情境

了解了@Transaction注解的使用方式,便可以在项目中使用事务来保证业务处理的原子性了。但是,实际使用过程中往往会遇到很多情境,导致事务并没有生效,在此总结一下失效的情况。

  • @Transaction注解加在了非public的方法上,此时Spring不会进行事务配置
  • 同一个类中的方法调用了另一个方法,而调用方法没有使用注解,导致被调用方法即使使用注解也是无效的
    • 项目中就是碰到了这种情况才导致了事务没有生效
  • 使用@Transaction注解的方法中,产生的异常使用catch进行了处理,导致异常没有被抛出,此时事务不会回滚
  • @Transaction注解的异常范围设置不对,出现了范围之外的异常,此时事务不会回滚
    • 默认是遇到RuntimeException异常回滚

3. 总结

@Transaction注解实现事务,一个小小的注解,使用起来也非常方便,但是存在的问题还是不少的,要想用好事务还是要认真的了解@Transaction注解实现的机制呀。