@Transactional又双叒叕失效了?


引言

又到了喜闻乐见的 @Transactional 注解失效环节


正文

md,@Transactional注解又又又又“失效”了。

这次是在单元测试中失效了:单元测试的时候,为啥业务代码中的遇到异常没有回滚?明明业务代码中加了 @Transactional 注解的…

请注意我的描述,是业务代码的事务,不是单元测试的事务。在单元测试中我开启了事务,单元测试方法上也加了 @Rollback 注解,所以单元测试的回滚是没有问题的。

事故现场描述

业务代码简略如下:

@Override
@Transactional(rollbackFor = Exception.class)
public boolean faceDelete(List<Long> uidList) {
   	...
    
    // 删除recognition_user表中的数据
    Assert.isTrue(recognitionUserMapper.deleteBatchIds(uidList) == uidList.size(), "删除人脸信息数据失败");
    // 删除人脸特征数据(midList的获取我省略了)
    Assert.isTrue(faceRecognition.faceDelete(midList), "删除人脸特征向量数据失败");
    return true;
}

单元测试代码简略如下:

@Rollback
@Test
public void faceDeleteTest() {
    List<Long> uidList = new LinkedList<>();
    uidList.add(11L);

    ...

    // mock: 测试删除人脸特征数据(即业务代码中的[faceRecognition.faceDelete(midList)])失败时的情况
    when(faceRecognition.faceDelete(any())).thenReturn(false);
    Assertions.assertThrows(IllegalArgumentException.class, () -> faceService.faceDelete(uidList));
    // 测试事务回滚,此时数据库中应该存在id为11的数据才对
    Assertions.assertNotNull(recognitionUserMapper.selectById(11));
}

但是,很不幸的,上面的单元测试没有通过,Assertions.assertNotNull(recognitionUserMapper.selectById(11))断言失败了,由此可以看出,业务代码中的事务并没有回滚,因为 id 为 11 的那条数据被删掉了。请注意,这里我用了“还”🧐

解释

业务代码中的事务为什么并没有回滚,是因为单元测试方法 faceDeleteTest 里调用了业务方法 faceDelete,而 faceDelete 上的 @Transactional 注解会复用外部单元测试方法中的事务,所以,直到单元测试方法结束之后,业务代码中的事务才会回滚

解决方法

至于解决方法,可以通过在单元测试方法上添加 @Transactional(propagation=Propagation.NOT_SUPPORTED) 来解决这个问题

@Test
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public void faceDeleteTest() { ... }

使用 Propagation.NOT_SUPPORTED 会取消外部事务, 这时业务方法只会使用自己的事务。但是单元测试方法里的数据库操作,例如添加一些测试数据是不会回滚,有可能会污染数据库。推荐使用单元测试时使用 H2 数据库,并且数据不用持久化。


最后

看到单元测试爆红,拳头就硬了起来 👊

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值