«

spring事务里面开启线程插入报错了会回滚吗

时间:2024-6-14 09:34     作者:韩俊     分类: Java


这篇文章主要讲解了“spring事务里面开启线程插入报错了会回滚吗”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“spring事务里面开启线程插入报错了会回滚吗”吧!

1.前言

一道非常有意思的面试题目。大概是这样子的,如果在一个事务中,开启线程进行插入更新等操作,如果报错了,事务是否会进行回滚。

2.代码

示例1

@RequestMapping("/test/publish/submit")
public String testPublish2() {

    log.info("start...");
    transactionTemplate.execute(new TransactionCallback<String>() {
        @Override
        public String doInTransaction(TransactionStatus status)  {

            TElement element = new TElement();
            element.setfElementId(10L);
            element.setfElementName("111");
            mapper.insertSelective(element);

            element = new TElement();
            element.setfElementId(10L);
            element.setfElementName("222");
            mapper.insertSelective(element);

            return "OK";
        }
    });
    log.info("end...");

    return "ok";
}

示例2

@RequestMapping("/test/publish/submit2")
public String testPublish3() {

    log.info("start...");
    transactionTemplate.execute(new TransactionCallback<String>() {
        @Override
        public String doInTransaction(TransactionStatus status)  {
            es.submit(() -> {
                TElement element = new TElement();
                element.setfElementId(10L);
                element.setfElementName("111");
                mapper.insertSelective(element);
            });

            es.submit(() -> {
                TElement element = new TElement();
                element.setfElementId(10L);
                element.setfElementName("222");
                mapper.insertSelective(element);
            });

            return "OK";
        }
    });
    log.info("end...");

    return "ok";
}

3.结论

示例1

element.setfElementId(10L); 为主键。SQL在第一次插入id=10的时候是没有问题的,在第二次插入id=10的时候,由于主键冲突了,导致报错,然后整个事务都会进行回滚,这是没有问题的。是spring的事务帮助我们来进行回滚等操作的。我们可以看到如下代码,他是对整个result = action.doInTransaction(status);进行了try catch。如果抛异常,就会回滚

@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
    Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

    if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
        return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
    }
    else {
        TransactionStatus status = this.transactionManager.getTransaction(this);
        T result;
        try {
            result = action.doInTransaction(status);
        }
        catch (RuntimeException | Error ex) {
            // Transactional code threw application exception -> rollback
            rollbackOnException(status, ex);
            throw ex;
        }
        catch (Throwable ex) {
            // Transactional code threw unexpected exception -> rollback
            rollbackOnException(status, ex);
            throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
        }
        this.transactionManager.commit(status);
        return result;
    }
}

示例2

示例2首先是transactionTemplate.execute是一个主main线程。然后在第一个子线程插入了一个数据,第二个子线程也插入了一个数据。那么现在就是有三个线程,一个是main线程,一个是A线程,一个是B线程。
main线程正常执行不报错,A线程正常插入不报错,B线程由于主键冲突报错。
我们可以通过上面action.doInTransaction(status);看出来,他对这块代码进行了try catch。也就是主线程进行了try catch。那么也就是只要主线程没有报错,这个事务就不会被捕获,也就不会回滚了。无论你A,B还是CDEFG子线程出问题了,只要不影响main线程,那事务就不会回滚呢?
因此我们可以得出一个结论,在示例2中,A线程会插入成功,B线程插入失败,事务不会回滚,最终插入成功。这个其实与我们平常的想法所违背了。
因此如果想要主线程抛出异常,得让主线程感知到子线程异常了,主动地去throw异常。比如我们可以设置一个flag,子线程报错了 flag=true。主线程检测到flag为true,就主动抛出一个exception

标签: java spring

热门推荐