事务传播行为
参考:https://segmentfault.com/a/1190000013341344
例子:
准备 对日志log表的service层操作
package com.wing.my.cloud.system.modular.system.service; import com.wing.my.cloud.system.modular.system.definedLog.entity.DefineLogEntity; import com.wing.my.cloud.system.modular.system.mapper.DefineLogMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * <p> * 测试事务A * </p> * */ @Service public class Transaction1Service { @Resource DefineLogMapper defineLogMapper; @Transactional(propagation = Propagation.REQUIRED) public void addLog_Required(){ DefineLogEntity defineLogEntity = new DefineLogEntity(); defineLogEntity.setClassName("Transaction1Service"); defineLogEntity.setMethodName("addLogRequired"); defineLogMapper.insert(defineLogEntity); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void addLog_REQUIRES_NEW(){ DefineLogEntity defineLogEntity = new DefineLogEntity(); defineLogEntity.setClassName("Transaction1Service"); defineLogEntity.setMethodName("addLogREQUIRES_NEW"); defineLogMapper.insert(defineLogEntity); } @Transactional(propagation = Propagation.NESTED) public void addLog_NESTED(){ DefineLogEntity defineLogEntity = new DefineLogEntity(); defineLogEntity.setClassName("Transaction1Service"); defineLogEntity.setMethodName("addLogNESTED"); defineLogMapper.insert(defineLogEntity); } }
对user表的service层的操作
package com.wing.my.cloud.system.modular.system.service; import com.wing.my.cloud.system.modular.system.entity.User; import com.wing.my.cloud.system.modular.system.mapper.UserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * <p> * 测试事务B * </p> * */ @Service @Slf4j public class Transaction2Service { @Resource UserMapper userMapper; @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED(){ User user = User.builder() .account("张三REQUIRED") .status("ENABLE") .build(); userMapper.insert(user); } @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException(){ User user = User.builder() .account("张三REQUIRED运行时异常").status("ENABLE").build(); userMapper.insert(user); throw new RuntimeException("RuntimeException是运行时异常,事务自动回滚"); } /** * 因为异常被吃掉,所以走不成事务,不会回滚 */ @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_Try(){ User user = User.builder() .account("张三REQUIRED运行时异常被cacth掉").status("ENABLE").build(); userMapper.insert(user); try { throw new RuntimeException("RuntimeException是运行时异常,事务自动回滚"); } catch (Exception e) { log.error("异常被吃掉"); } } @Transactional(noRollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_NoRollbackFor(){ User user = User.builder() .account("张三REQUIRED运行时异常").status("ENABLE").build(); userMapper.insert(user); throw new RuntimeException("加上noRollbackFor也不会走事务。"); } @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_Exception() throws Exception{ User user = User.builder() .account("张三REQUIRED非运行时异常").status("ENABLE").build(); userMapper.insert(user); throw new Exception("Exception是非运行时异常,事务失效"); } @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED) public void addUser_REQUIRED_Exception_RollbackFor() throws Exception{ User user = User.builder() .account("张三REQUIRED非运行时异常").status("ENABLE").build(); userMapper.insert(user); throw new Exception("Exception是非运行时异常,事务失效"); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void addUser_REQUIRES_NEW(){ User user = User.builder() .account("李四REQUIRES_NEW") .status("ENABLE") .build(); userMapper.insert(user); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void addUser_REQUIRES_NEW_Exception(){ User user = User.builder() .account("李四REQUIRES_NEW运行时异常").build(); userMapper.insert(user); throw new RuntimeException("RuntimeException是运行时异常,事务自动回滚"); } @Transactional(propagation = Propagation.NESTED) public void addUser_NESTED(){ User user = User.builder() .account("李四NESTED") .status("ENABLE") .build(); userMapper.insert(user); } @Transactional(propagation = Propagation.NESTED) public void addUser_NESTED_Exception(){ User user = User.builder() .account("李四NESTED运行时异常").status("ENABLE").build(); userMapper.insert(user); throw new RuntimeException("RuntimeException是运行时异常,事务自动回滚"); } }
对事务的操作
package com.wing.my.cloud.system.modular.system.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * <p> * 事务嵌套 * </p> * */ @Service @Slf4j public class Transaction3Service { @Resource Transaction1Service transaction1Service; @Resource Transaction2Service transaction2Service; /** * log添加一条数据 * user添加一条数据 * @throws Exception */ public void exception失效() throws Exception{ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_Exception(); log.info("只有在返回的是不受检查异常才会有效,exception是检查异常。不会走事务"); } /** * log添加一条数据 * @throws Exception */ public void exception失效但是事务_加上rollbackFor可以走事务() throws Exception{ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_Exception_RollbackFor(); log.info("Exception加上rollbackFor会走事务"); } /** * log添加一条数据 */ public void RuntimeException可以走事务(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException(); log.info("只有在返回的是不受检查异常才会有效,RuntimeException是非检查异常。会走事务"); } /** * log添加一条数据 * user添加一条数据 */ public void RuntimeException可以走事务_加上noRollbackFor不会走事务(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException_NoRollbackFor(); log.info("noRollbackFor不会走事务"); } public void 异常被吃掉(){ transaction2Service.addUser_REQUIRED_RuntimeException_Try(); } /** * REQUIRED * 如果当前没有事务,就新建一个事务。已经存在事务,就加入到这个事务中 * 外层没有事务,内层Log,内层User有事务。 * 内层事务没有异常。外层有异常。 * 外层回滚,内层不会回滚。内层的事务是单独运行的。 * * log添加一条数据 * user添加一条数据 */ public void REQUIRED_外层无事务_两个内层事务独立_都无异常(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED(); log.info("两个事务" + "外层没有事务,内层有事务.内层都没有异常。外层有异常。外层回滚,内层不会回滚。因为不在一个事务中"); throw new RuntimeException(); } /** * log添加一条数据 */ public void REQUIRED_外层无事务_两个内层事务独立_USER有异常(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException(); log.info("两个事务" + "外层没有事务,内层有事务.内层事务User有异常。外层有异常。外层回滚,内层Log没有异常,不会回滚。内层User有异常,回滚.因为不在一个事务中"); throw new RuntimeException(); } //-----外层不开事务。REQUIRED修饰的内层事务会新开自己的事务。互相独立,互不干扰。 /** * 都不添加数据 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外层有事务_两个内层事务加入到外层事务() { transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED(); log.info("一个事务" + "外层有事务,内层两个都有事务,都没有异常.事务Log和事务User都加入到外层的事务中。外层回滚,内层也回滚"); throw new RuntimeException(); } /** * 都不添加数据 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外层有事务_两个内层事务加入到外层事务_事务USER有异常_外层感知到(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException(); log.info("一个事务" + "外层有事务,内层加入外层事务。内层抛出异常回滚,外层感知到异常使整个事务都回滚"); throw new RuntimeException(); } /** * 都不添加数据 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外层有事务_两个内层事务加入到外层事务_外层有异常_事务USER有异常_被catch吃掉_外层感知不到(){ transaction1Service.addLog_Required(); try { transaction2Service.addUser_REQUIRED_RuntimeException(); } catch (Exception e) { log.error("一共一个事务" + "内层事务的异常被吃掉,外层感知不到异常。但是本身外层有异常,外层回滚。内层也回滚"); } throw new RuntimeException(); } //-----外层开启事务。REQUIRED修饰的内层事务会加入到外层事务中。所有的事务都在同一个事务中了。只要有一个方法回滚。所有的都回滚 /** * 都不添加数据 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外层有事务_两个内层事务加入到外层事务_事务USER有异常_被catch吃掉_外层感知不到(){ transaction1Service.addLog_Required(); try { transaction2Service.addUser_REQUIRED_RuntimeException(); } catch (Exception e) { log.error("一共一个事务" + "内层事务的异常被吃掉,外层感知不到异常。但是当前事务已经被标记为rollbackOnly了,所以无法提交"); } } //-----外层开启事务后,内层事务加入到外层事务。内层方法抛出异常回滚,即使被trycatch吃掉。不被外层方法感知到。整个事务依旧回滚。 // ------因为在同一个事务中,只要有异常,都会被察觉到。然后执行回滚。 /** * REQUIRES_NEW * 新建事务,如果当前存在事务,就把当前事务挂起 * * 外层没有事务,两个内层都是在自己的事务中 。外层抛出异常回滚不会影响内层的方法。 * log插入一条数据 * user插入一条数据 */ public void REQUIRES_NEW_两个内层事务独立(){ transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW(); log.info("一共两个事务" + "外层没有事务,内层有事务。两个内层都没有异常。外层有异常。外层回滚,内层不会有回滚"); throw new RuntimeException(); } /** * log插入一条数据 */ public void REQUIRES_NEW_两个内层事务独立_USER有异常_不会影响LOG事务() { transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW_Exception(); log.info("一共两个事务。" + "外层没有事务,内层两个都有事务,logRequires_new事务没有异常,插入成功" + "userRequires_new有异常,回滚"); throw new RuntimeException(); } //外层未开启事务的情况下,REQUIRES_NEW修饰的内层事务 新开自己的事务。互相独立。互不干扰。 /** * log_requires_new插入一条 * user_requires_new插入一条 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRES_NEW_内层事务独立存在不加入到外层事务中(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW(); log.info("一共3个事务。" + "logRequired会加入到外层的事务中。logRequires_new和userRequires_new都是单独的事务。外层事务有异常,回滚。不会插入数据。" + "两个requires_new都是单独的事务,没有异常,插入成功。"); throw new RuntimeException(); } /** * log_requires_new插入一条数据 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRES_NEW_内层事务独立不会加入到外层事务中_USER事务有异常不会影响到外层和log事务(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW_Exception(); log.info("一共三个事务" + "logRequired会加入到外层的事务中。userRequiresNew返回异常,外层捕获到异常。logRequired回滚" + "logrequires_new是单独的事务,没有异常,插入成功。" + "userRequires_new是单独的事务,有异常,回滚。"); } /** * logRequired插入成功 * logRequires_new插入成功 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRES_NEW_内层事务独立_内层USER事务被吃掉(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); try { transaction2Service.addUser_REQUIRES_NEW_Exception(); } catch (Exception e) { log.error("一共有三个事务。" + "内层logRequired加入到外层事务中。因为userRequires_new返回的异常被吃掉。外层感知不到异常。没有回滚。插入成功" + "内层userRequires_new是单独的事务。没有异常,插入成功" + "内层userRequires_newException是单独的事务,有异常,进行回滚。不会插入成功"); } } /** * logRequires_new插入成功 * userRequires_new插入成功 */ @Transactional(propagation = Propagation.REQUIRES_NEW) public void REQUIRES_NEW新开事务_REQUIRED加入外层事务(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW(); log.info("一共3个事务。" + "外层一个。logRequired加入到外层事务。logRequires_new新创建一个事务.userRequires_new新创建一个事务" + "3个内层事务没有异常。但是logRequired加入到外层事务中了,外层事务有异常,不会插入成功。另外两个内层事务插入成功。" ); throw new RuntimeException(); } //-----外层开启事务。内层用REQUIRES_NEW修饰的方法依旧会开启独立事务。且与外层事务也独立,互相独立,互不干扰。 /** * 不添加 */ @Transactional(propagation = Propagation.REQUIRED) public void NESTED_内层事务是外层的子事务_外层有异常外层回滚_两个子事务都回滚(){ transaction1Service.addLog_NESTED(); transaction2Service.addUser_NESTED(); log.info("两个事务。" + "logNested是外部事务的子事务" + "userNested是外部事务的子事务" + "外层事务有异常,回滚。两个子事务都回滚"); throw new RuntimeException(); } /** * 不添加 */ @Transactional(propagation = Propagation.REQUIRED) public void NESTED_内层事务是外层事务的子事务_子事务有异常被外层感知到所有子事务都回滚(){ transaction1Service.addLog_NESTED(); transaction2Service.addUser_NESTED_Exception(); log.info("两个事务。" + "logNested是外部事务的子事务" + "userNested是外部事务的子事务" + "userNested有异常,外层感知到异常。外层回滚,两个子事务都回滚" ); } /** * log_nested添加成功 */ @Transactional(propagation = Propagation.REQUIRED) public void NESTED_内层方法是外层事务的子事务_子事务异常不被外层感知到有异常的子事务回滚_没有异常的子事务提交(){ transaction1Service.addLog_NESTED(); try { transaction2Service.addUser_NESTED_Exception(); } catch (Exception e) { log.error("两个事务。" + "logNested是外部事务的子事务" + "userNested是外部事务的子事务" + "userNested有异常,但是被catch捕获到。外层感知不到异常。userNested回滚" + "logNested没有异常。提交"); } } //------NESTED修饰的内层方法是外部事务的子事务,外层回滚。内层的都回滚。内层的独立存在,互不干扰 //--------外层没有事务的时候。也是新开事务,互相独立。互不干扰。 /** * userRequires_new插入成功 */ @Transactional(propagation = Propagation.REQUIRED) public void 组合(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRES_NEW(); transaction2Service.addUser_NESTED(); transaction2Service.addUser_NESTED_Exception(); log.info("两个事务" + "logRequired加入到外层事务,外层捕获到userNested返回的异常,回滚" + "userRequires_new新开事务。与外层没有影响" + "userNested和userNestedException都是外层的子事务,外层捕获到异常,进行回滚,两个子事务也进行回滚"); } }
总结:
一:分析事务
看外层。如果外层没有事务。那么去分析包含的方法中有没有加上事务,有几个方法加上了事务就开启了几个事务。这几个事务是互相独立,互不干扰的。
如果有事务。子事务中用REQUIRED 修饰的会加入到外层事务中。
子事务用REQUIRES_NEW 修饰的不会去搭理外层的事务。自己新开事务。
子事务用NESTED 修饰的是外层的子事务。如果外层事务回滚。外层事务下是所有子事务也回滚。
二:分析事务是否失效
2.1:抛出的是非运行时异常。
比如抛出Exception
但是可以加上rollbackFor
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
抛出RunTimeException。可以走事务,如果不想走事务的话可以加上noRollbackFor
@Transactional(noRollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
2.2:异常被catch掉
/** * 因为异常被吃掉,所以走不成事务,不会回滚 */ @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_Try(){ User user = User.builder() .account("张三REQUIRED运行时异常被cacth掉").status("ENABLE").build(); userMapper.insert(user); try { throw new RuntimeException("RuntimeException是运行时异常,事务自动回滚"); } catch (Exception e) { log.error("异常被吃掉"); } }
public void 异常被吃掉(){ transaction2Service.addUser_REQUIRED_RuntimeException_Try(); }
异常被吃掉解决方案。
@Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_Try(){ User user = User.builder() .account("张三REQUIRED运行时异常被cacth掉").status("ENABLE").build(); userMapper.insert(user); try { throw new RuntimeException("RuntimeException是运行时异常,事务自动回滚"); } catch (Exception e) { log.error("异常被吃掉"); //解决方案一:在catch中加上 throw new RuntimeException() 把异常给抛出去。 //throw new RuntimeException(); //解决方案二: 在catch中手动回滚 //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }