spring事务自身调用问题的理解

唉,Java面试经典八股文。

最近面试,又被问到了这个问题。网上一堆文章,几乎全是复制粘贴,千篇一律,想找到一篇有用的博文真的是太难了。

纸上得来终觉浅,绝知此事要躬行。

在经过了三四天的努力研究之后,终于醒悟。

在进入正题之前,我们先来点准备,先来理解一下事务的传播机制,如果不能深入理解事务的传播机制,是无法理解事务的自身调用失效的问题的。

通过查看源码,我们可以知道spring事务的默认传播机制是Propagation.REQUIRED。这个是什么意思呢?这个的意思就是,如果当前有事务就加入,没有就开启一个新的事务。

我们再来看另一个事务传播机制,Propagation.REQUIRES_NEW。是不是看着很眼熟?对的,就是它,经常出现在现有的那些关于事务自身调用的文章里面。它是什么意思呢?它的意思是,开启一个新的事务。什么叫开启一个新的事务呢?就是说,它是一个独立的新事务。如果外层有事务,那它不属于外层的事务,不受外层事务的影响;如果外层没有事务,它就创建一个新的事务。看着有点难理解,多读几遍就懂了,后面我们会结合着代码具体说这个。

上代码。

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao dao;

    @Override
    //@Transactional
    public void insert() {
        this.save();
        //throw new RuntimeException();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save() {
        dao.insert("test");
        dao.update();
        throw new RuntimeException("aaa");
    }

}
如图所示,我们的insert方法调用了自身的save方法。那么当我们的controller调用insert方法时事务会生效吗?如果事务生效的话,数据库的数据看着不会有影响。因为,我们在insert和update以后抛出了一个运行时异常,触发了事务的回滚机制,会导致回滚。如果事务不生效,数据库的数据会有一条新增,一条修改。

实践是检验真理的唯一标准。

运行代码后,我们坚持数据库,发现数据库里新增了一条数据,修改了一条数据。事实雄辩地证明,事务失效了。

那么,我们修改一下代码,修改成这样:

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao dao;

    @Override
    @Transactional
    public void insert() {
        this.save();
        //throw new RuntimeException();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save() {
        dao.insert("test");
        dao.update();
        throw new RuntimeException("aaa");
    }

}

我们在insert方法上加上了@Transactional注解,把它交给spring管理。再运行一下代码。我们惊奇的发现,数据库的数据没有变动。这是不是证明了事务生效了呢?错!

哎呀,我就是被这个困惑了好久。

请仔细阅读代码,我们发现,insert方法上,只是加了@Transactional,而save方法上加的是@Transactional(propagation = Propagation.REQUIRES_NEW)。再想想我们前面说的事务的传播机制。propagation = Propagation.REQUIRES_NEW的意思是开启一个新的事务,不受外部事务影响。但事实是,它受到了外部事务的影响。由于save方法抛出了一个运行时异常,被insert方法捕获到,触发了回滚机制,导致数据库里的数据没有影响。这是个很迷惑的点,会让你误以为事务生效了,因为有运行时异常,事务回滚,完全符合事务生效的约定啊。但是呢,生效的事务是insert上的事务,并不是save上的事务。save上的事务是失效了的。这样看,不是很明显,我们再修改一下代码

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private TestDao dao;

    @Override
    @Transactional
    public void insert() {
        this.save();
        throw new RuntimeException();
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save() {
        dao.insert("test");
        dao.update();
        //throw new RuntimeException("aaa");
    }

}

改成这样,让insert方法抛异常。运行后发现,数据库数据没有改变。这样对吗?按理说,save方法上的事务是要新开启一个事务的,无论insert事务是否成功,都不影响save的事务。而事实是,save方法的事务被insert方法的事务一起接管了。

这样就证明了自身调用时事务失效了。至于怎么避免,网上有的是,不想赘述。

而实际开发中,更多的时候,我们是想让insert方法和save方法在一个事务下的,所以,只要被controller调用的那个方法上有事务,就能达到我们想要的那种事务效果。在我看来,这种事务失效的问题,纯粹就是为了面试产生的。典型的应试产物。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值