当前位置: 首页 > 后端技术 > Java

面试惊喜88:加入事务和嵌套事务有什么区别?

时间:2023-04-01 21:58:15 Java

加入事务和嵌套事务是指Spring事务传播机制中加入事务(REQUIRED)和嵌套事务(NESTED)的区别。两者看似相似,实则完全不同。那么它们之间有什么区别呢??接下来我们一起来看看吧。Spring事务传播机制是指包含多个事务的方法相互调用时,事务如何在方法之间传播。Spring事务传播机制分为3类,共7个层次,如下图所示:其中,支持当前事务的REQUIRED是加入(当前)事务,NESTED是嵌套(当前)交易。本文将讨论两者之间的区别。1.加入事务加入事务REQUIRED是Spring事务的默认传播级别。所谓加入当前交易,是指如果当前有交易则加入交易;如果当前没有交易,则创建一个新交易。这里要讨论的是第一种情况,即当前有事务时它与嵌套事务的区别。下面我们通过一个例子来看看加入交易的使用和执行特点。我们要实现的是用户添加功能,但是在添加用户的时候,需要分别往用户表和日志表中插入一条数据。UserController实现代码如下:@Transactional(propagation=Propagation.REQUIRED)@RequestMapping("/add")publicintadd(UserInfouserInfo){intresult=0;intuserResult=userService.add(userInfo);System.out.println("用户添加结果:"+userResult);如果(userResult>0){LogInfologInfo=newLogInfo();logInfo.setName("添加用户");logInfo.setDesc("添加用户结果:"+userResult);intlogResult=logService.add(logInfo);System.out.println("日志添加结果:"+logResult);结果=1;}returnresult;}从上面的代码可以看出,添加用户使用了一个事务,并且设置事务传播机制为REQUIRED(加入一个事务),这个controller调用的UserService的实现代码如下:@事务性(传播=传播。必需的)公共int添加(UserInfouserInfo){intresult=userMapper.add(userInfo);returnresult;}从上面的代码可以看出,它同样使用了事务,并且设置了事务的传播机制是REQUIRED,LogService也是类似的实现代码:@Transactional(propagation=Propagation.REQUIRED)publicintadd(LogInfologInfo){intresult=logMapper.add(logInfo);尝试{整数=10/0;}catch(Exceptione){//手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}returnresult;}从上面的代码可以看出,我们在设置事务传播机制的同时,也在程序中主动设置了异常。上面程序的执行结果如下图所示:从上面的结果我们可以看到输出:我们设置好加入事务的事务传播机制后,程序的执行结果是回滚了事务用户表和日志表。2、嵌套事务嵌套事务是指事务传播层中的NESTED。所谓嵌套当前事务,是指如果有当前事务,则创建一个事务作为当前事务的嵌套事务运行;如果没有当前事务,则此值等同于REQUIRED。当然,本文我们研究的重点也是第一种情况,即当前事务前提下的嵌套事务和联合事务的区别。那么接下来我们将上面代码中的事务传播机制改为NESTED,其实现代码如下。UserController的实现代码如下:@Transactional(propagation=Propagation.NESTED)@RequestMapping("/add")publicintadd(UserInfouserInfo){intresult=0;intuserResult=userService.add(userInfo);System.out.println("添加用户结果:"+userResult);如果(userResult>0){LogInfologInfo=newLogInfo();logInfo.setName("添加用户");logInfo.setDesc("添加用户结果:"+userResult);intlogResult=logService.add(logInfo);System.out.println("日志添加结果:"+logResult);结果=1;}returnresult;}UserService实现代码如下:@Transactional(propagation=Propagation.NESTED)publicintadd(UserInfouserInfo){intresult=userMapper.add(userInfo);returnresult;}LogService实现代码如下:@Transactional(propagation=Propagation.NESTED)publicintadd(LogInfologInfo){intresult=logMapper.add(logInfo);尝试{整数=10/0;}catch(Exceptione){//手动返回回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}returnresult;}运行上面程序的执行结果如下图所示:从上面的结果可以看出,设置嵌套事务的事务传播级别后,程序执行了部分事务回滚,用户表添加的事务不回滚,但是日志表的事务回滚Propagationlevel,如果当前没有事务,两者的行为是一样的;但如果有事务,加入事务的事务传播层遇到异常后会回滚所有事务;当到达异常时,只执行部分事务回滚。4.嵌套事务实现原理很容易理解所有事务都回滚。这是事务原子性的体现,但是嵌套事务中的部分事务回滚是如何实现的呢?嵌套事务只能实现部分事务回滚,因为数据库中有保存点(savepoint)的概念。以MySQL为例,嵌套事务相当于新建了一个保存点,只是回滚到当前保存点,所以不会影响之前的事务。这可以在官方MySQL文档中找到:https://dev.mysql.com/doc/refman/5.7/en/savepoint。html和REQUIRED添加到当前事务中,没有创建事务的保存点。因此,如果发生回滚,则整个事务都会回滚。这就是嵌套事务和联合事务的区别。存档点就像玩游戏时的“游戏存档”。如果设置了游戏存档,即使当前关卡失败,也可以在之前的存档点继续游玩,而不是从头开始游戏。总结加入事务(REQUIRED)和嵌套事务(NESTED)是事务传播机制中的两个传播层次。如果没有当前事务,两者的行为是一致的;但如果有当前事务,则加入该事务事务传播级在遇到异常时回滚所有事务,而嵌套事务则回滚部分事务。嵌套事务之所以能够回滚部分事务,是因为数据库中有一个保存点的概念。与嵌套事务新建保存点相比,如果出现异常,只需要回滚到保存点即可。实现了部分事务的回滚。判断是非在自己,名誉在别人,得失在人数。公众号:Java面试真题分析面试合集:https://gitee.com/mydb/interview