Spring事务传播机制是指当包含多个事务的方法相互调用时,事务如何在方法间传播。既然是“交易传播”,那么交易的数量应该是两个以上。Spring事务传播机制的诞生就是为了指定多个事务在传播过程中的行为。比如方法A启动了一个事务,在执行过程中调用了启动事务的方法B,那么方法B的事务是否应该加入到A的事务中呢?还是两个事务执行互不影响,还是B事务嵌套在A事务中执行?那么这时候就需要一种机制来指定和约束这两个事务的行为,这就是Spring事务传播机制所要解决的问题。什么是Spring事务传播机制?Spring事务传播机制可以使用@Transactional(propagation=Propagation.REQUIRED)来定义,Spring事务传播机制的层次包括以下7种:Propagation.REQUIRED:默认的事务传播层次,表示如果一个事务当前存在,加入交易;如果没有当前事务,则创建一个新事务。Propagation.SUPPORTS:如果有当前事务,则加入该事务;如果没有当前事务,则继续以非事务方式运行。Propagation.MANDATORY:(强制:Mandatory)如果当前有事务,则加入事务;如果没有当前事务,则抛出异常。Propagation.REQUIRES_NEW:表示创建了一个新事务,如果当前存在事务,则挂起当前事务。也就是说不管外部方法是否启动事务,被Propagation.REQUIRES_NEW修饰的内部方法都会启动自己的事务,并且开启的事务相互独立,互不干扰。Propagation.NOT_SUPPORTED:以非事务模式运行,如果有当前事务,则暂停当前事务。Propagation.NEVER:以非事务方式运行,如果当前存在事务则抛出异常。Propagation.NESTED:如果当前有事务,则创建一个事务作为当前事务的嵌套事务运行;如果当前没有事务,这个值相当于PROPAGATION_REQUIRED。以上7种通信机制,按照“是否支持当前交易”的维度,可以分为以下3类:看到这里,可能有人会说:说了这么多我也听不懂。不记得了?我应该怎么办?没关系,下面我们用一个例子来说明这三种事务传播机制的区别。以情侣是否要买房为例,我们可以将以上三种交易沟通机制看作恋爱中的三种女生:平凡、坚强、懂事。这三类女生如下图所示:“女生”,这里的业务指的是“房子”,分为3种(普通女生):Propagation.REQUIRED(需要有房子):如果我们有房子,我们一起住,没有房子,我们一起赚钱买房。Propagation.SUPPORTS(youcanhaveahouse):如果你们有房子,你们就可以住在一起。如果你们没有房子,可以一起租房子。Propagation.MANDATORY(必须有房子):有房子就住在一起,没有房子就分手。不支持时政的“妹子”也分为3种(强势型或事业型):传播。REQUIRES_NEW:不要你的房子,必须一起赚钱买房。Propagation.NOT_SUPPORTED:不要你的房子,必须一起租房子。Propagation.NEVER:一定要一起租房子,有房就分手。最后一个是嵌套事务Propagation.NESTED,属于懂事的闺蜜。如果有房子,可以以房子为基础做点小生意,卖花生、水果等,如果生意成功,再继续发展;如果你失败了,至少你还有房子;如果你没有房子,没关系,我们一起赚钱买房子吧。事务传播机制的使用和演示接下来,我们将演示事务传播机制的使用,以下面三种最典型的事务传播级别为例:REQUIRED,支持当前事务;REQUIRES_NEW不支持当前事务;嵌套事务NESTED。我们分别来看一下。事务传播机制的示例需要以下两个表:--usertableCREATETABLE`user`(`id`int(11)NOTNULLAUTO_INCREMENT,`name`varchar(255)COLLATEutf8mb4_binDEFAULTNULL,`password`varchar(255)COLLATEutf8mb4_binDEFAULTNULL,`createtime`datetimeDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`)USINGBTREE)ENGINE=InnoDBAUTO_INCREMENT=6DEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_binROW_FORMAT=DYNAMICtableloglog`(`id`int(11)NOTNULLAUTO_INCREMENT,`content`textNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_bin;创建一个SpringBoot项目,核心业务代码3个,分别为:UserController、UserServcie、LogService。在UserController中调用UserService添加用户,调用LogService添加日志。REQUIRED使用演示REQUIRED来支持当前事务。UserController的实现代码如下,其中save方法启动事务:@RestControllerpublicclassUserController{@ResourceprivateUserServiceuserService;@Resource私有日志服务日志服务;@RequestMapping("/save")@TransactionalpublicObjectsave(Useruser){//插入用户操作userService.save(user);//插入日志logService.saveLog("用户插入:"+user.getName());返回真;}}UserService实现代码如下:@ServicepublicclassUserService{@ResourceprivateUserMapperuserMapper;@Transactional(propagation=Propagation.REQUIRED)publicintsave(Useruser){returnuserMapper.save(user);}}LogService实现代码如下:@ServicepublicclassLogService{@ResourceprivateLogMapperlogMapper;@Transactional(propagation=Propagation.REQUIRED)publicintsaveLog(Stringcontent){//发生异常。诠释我=10/0;返回logMapper.saveLog(内容);}}执行结果:程序报错,两张表都没有插入数据。执行过程说明:首先正常执行UserService中添加用户的方法。LogService保存日志程序并报错。因为使用了UserController中的全局事务,回滚了整个事务,第1步的操作也回滚了。所以数据库中没有添加任何数据。REQUIRED_NEWdemoREQUIRED_NEW不支持当前事务。UserController实现代码:@RequestMapping("/save")@TransactionalpublicObjectsave(Useruser){//插入用户操作userService.save(user);//插入日志logService.saveLog("用户插入:"+user.getName());returntrue;}UserService实现代码:@ServicepublicclassUserService{@ResourceprivateUserMapperuserMapper;@Transactional(propagation=Propagation.REQUIRES_NEW)publicintsave(Useruser){System.out.println("执行保存方法。");返回userMapper.save(user);}}LogService实现代码:@ServicepublicclassLogService{@ResourceprivateLogMapperlogMapper;@Transactional(propagation=Propagation.REQUIRES_NEW)publicintsaveLog(Stringcontent){//异常发生inti=10/0;返回logMapper.saveLog(内容);}}程序执行结果:成功向User表添加了一条用户数据,但是Log表执行失败,没有添加任何数据,但不影响UserControllerTransaction执行。从上面的结果可以看出,LogService使用了一个单独的事务。虽然LogService中的事务执行失败了,但是不影响UserController和UserService中的事务。NESTED用法演示NESTED是一种嵌套事务。UserController实现代码如下:@RequestMapping("/save")@TransactionalpublicObjectsave(Useruser){//插入用户操作userService.save(user);returntrue;}UserService实现代码如下:@Transactional(propagation=Propagation.NESTED)publicintsave(Useruser){intresult=userMapper.save(user);System.out.println("执行保存方法。");//插入日志logService.saveLog("用户插入:"+user.getName());returnresult;}LogService实现代码如下:@Transactional(propagation=Propagation.NESTED)publicintsaveLog(Stringcontent){//异常发生inti=10/0;返回logMapper.saveLog(内容);最后执行的结果是,user表和log表都没有添加数据。执行过程说明:在UserController中调用UserService的添加用户方法,UserService使用NESTED循环嵌套事务,成功执行添加用户方法。UserService中调用了LogService的Add方法,LogService使用了NESTED循环嵌套事务,但是方法执行过程中出现异常,回滚当前事务。因为UserService使用了嵌套事务,回滚的事务是全局的,也就是说UserService中添加用户的方法也被回滚了,最终的执行结果是user表和log表都没有添加任何数据.总结Spring事务传播机制就是当包含多个事务的方法相互调用时,事务如何在方法之间传播。有7个事务传播级别,支持当前事务:REQUIRED、SUPPORTS、MANDATORY;不支持当前事务:REQUIRES_NEW、NOT_SUPPORTED、NEVER和嵌套事务NESTED,其中REQUIRED是默认的事务传播级别。判断是非在自己,名誉在别人,得失在人数。公众号:Java面试真题分析面试合集:https://gitee.com/mydb/interview
