
上周,一同事看到我去年写的一些代码,@Transactional 加上了 rollbackFor,就问我为什么。我当时和他解释了一番,这里我分享出来,希望能够帮助到更多的人。
彻底弄懂@Transactional和@Transactional(rollbackFor = Exception.class)的区别到底在哪里!
首先我在 Mysql 中准备了一条数据,如下所示:

下面我们就开始简单粗暴的测试了。
delflag修改为 0,简单的准备一下 sql。 <update id="test">
UPDATE tbl_users set delflag='0' where account='admin'
</update>
@Transactional代码如下。大家都知道2/0必会抛出异常@Override
@Transactional
public Ret test(){
int i = articleMapper.test();
int a = /;
if(i > ){
ResultUtil.success();
}
return ResultUtil.error();
}
当i=1说明更新成功,别着急咱们继续断点往下面走。

java.lang.ArithmeticException: /by zero
ArithmeticException这个异常类是继承了RuntimeException类的当我们在使用@Transactional且不加rollbackFor,然后抛出上面的异常时,数据库回滚了。
原因是:@Transactional默认回滚的的异常就RuntimeException类型的。只要是RuntimeException类型的,和它子类型的异常,默认的都能回滚。

RuntimeException这个类里面一探究竟。我们发现RuntimeException又是继承Exception的。大多数自定义异常类基本都是继承RuntimeException包括刚才上面的java.lang.ArithmeticException异常。
所以只要是RuntimeException和RuntimeException下面的子类抛出的异常@Transactional都是可以回滚的。


下面我们再试试@Transactional不能回滚的异常。代码如下:
我们直接先用try catch来捕获异常,然后在catch里面自定义抛出Exception异常。
@Override
@Transactional
public Ret test() throws Exception {
int i = articleMapper.test();
try {
int a = / ;
} catch (Exception e) {
throw new Exception();
}
if (i > ) {
ResultUtil.success();
}
return ResultUtil.error();
}
ok 直接,抛出的异常是我们指定的java.lang.Exception异常。我们去看看数据库:

数据库被更新成0了,说明@Transactional并不能回滚Exception异常。

总结一下:@Transactional只能回滚RuntimeException和RuntimeException下面的子类抛出的异常,不能回滚Exception异常。
如果需要支持回滚Exception异常请用@Transactional(rollbackFor = Exception.class)。
这里如果是增删改的时候我建议大家都使用@Transactional(rollbackFor = Exception.class)。
补充一下@Transactional(rollbackFor = Exception.class)一些失效的场景。
public修饰try catch捕获了异常(没有在catch里面手动抛出异常)@Service(也就是没有被Spring管理)具体有好几种,我历史文章中有过详细的介绍,这里就不在细说了。