这篇文章主要讲解了“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