【Spring事务详解】--- 1.事务传播的案例演示
文章目录
- 前言
- 事务的传播类型介绍
- 案例演示数据说明
- REQUIRED
- SUPPORTS
- MANDATORY
- REQUIRES_NEW
- NOT_SUPPORTED
- NEVER
- NESTED
-
- NESTED与REQUIRES_NEW的区别
- NESTED实现方式
前言
Spring事务详解连载
【Spring事务详解】— 1.事务传播的案例演示
【Spring事务详解】— 2.事务应用的注意事项
【Spring事务详解】— 3.事务失效的八种场景
【Spring事务详解】— 4.事务管理器的架构分析
【Spring事务详解】— 5.事务管理器TransactionSynchronizationManager分析
【Spring事务详解】— 6.事务创建的流程分析
【Spring事务详解】— 7.事务提交、回滚的流程分析
【Spring事务详解】— 8.beforeCommit、beforeCompletion、afterCommit、afterCompletion实现分析
事务的传播类型介绍
REQUIRED:支持当前事务,如果当前不存在则新开启一个事务(默认配置)SUPPORTS:支持当前事务,如果当前不存在事务则以非事务方式执行MANDATORY:支持当前事务,如果当前不存在事务则抛出异常REQUIRES_NEW:创建一个新事务,如果当前已存在事务则挂起当前事务NOT_SUPPORTED:以非事务方式执行,如果当前已存在事务则挂起当前事务NEVER:以非事务方式执行,如果当前已存在事务则抛出异常NESTED:如果当前存在事务,则在嵌套事务中执行,否则开启一个新事务
案例演示数据说明
数据表
CREATE TABLE `t1` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_c` (`c`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `t2` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx` (`c`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
<update id="updateT1"> update t1 set c = 2 where id = 1</update><update id="updateT2"> update t2 set c = 2 where id = 1</update>
REQUIRED
当调用T1Service
中的func
方法时,除了更新t1
表数据外,还会调用t2Service的func
方法,更新t2
表。
@Servicepublic class T1Service { @Resource private TestMapper testMapper; @Resource private T2Service t2Service; @Transactional public void func() { testMapper.updateT1(); t2Service.func(); }}
@Servicepublic class T2Service { @Resource private TestMapper testMapper; @Transactional public void func() { testMapper.updateT2(); int i = 1 / 0; }}
@Transactional
默认的传播方式就是REQUIRED
,所以当方法执行到int i = 1 / 0
时,会抛出异常,t1、t2
表中的数据都不会被修改。
SUPPORTS
t2Service
的func
方法现在没有事务了,t2Service
的func
方法配置上@Transactional(propagation = Propagation.SUPPORTS)
,当执行int i = 1 / 0
时,t1、t2
两张表数据都不会回滚,但如果配置成@Transactional(propagation = Propagation.REQUIRED)
,则t2
表数据会被回滚。
@Servicepublic class T1Service { @Resource private TestMapper testMapper; @Resource private T2Service t2Service; // @Transactional public void func() { testMapper.updateT1(); t2Service.func(); }}
@Servicepublic class T2Service { @Resource private TestMapper testMapper;/** * 数据不会回滚,因为当前没有事务,SUPPORTS会以非事务方式执行 */ @Transactional(propagation = Propagation.SUPPORTS) public void func() { testMapper.updateT2(); int i = 1 / 0; }}
MANDATORY
当t1Service
没有事务时,把t2Service
的func
方法,配置为@Transactional(propagation = Propagation.MANDATORY)
// t1Servicepublic void func() { testMapper.updateT1(); t2Service.func();}// t2Service@Transactional(propagation = Propagation.MANDATORY)public void func() { testMapper.updateT2(); int i = 1 / 0;}
抛出异常
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.14.jar:5.3.14]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.14.jar:5.3.14]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.14.jar:5.3.14]
REQUIRES_NEW
毫无疑问,t2
的数据不会被更新,当没有事务时,REQUIRES_NEW
会自己创建一个事务
// t1Servicepublic void func() { testMapper.updateT1(); t2Service.func();}// t2Service@Transactional(propagation = Propagation.REQUIRES_NEW)public void func() { testMapper.updateT2(); int i = 1 / 0;}
与REQUIRED有什么区别呢?
现在把抛出异常的地方放到t1Service
中
// t1Service@Transactionalpublic void func() { testMapper.updateT1(); t2Service.func(); int i = 1 / 0;}// t2Service@Transactional(propagation = Propagation.REQUIRES_NEW)public void func() { testMapper.updateT2();}
此时执行后,t2
的数据不会回滚,t1
的数据会回滚,因为t2
和t1
不是一个事务。
NOT_SUPPORTED
NOT_SUPPORTED
的效果就是无论异常是在t1Service还是t2Service都不会回滚t2
的数据。
// t1Service@Transactionalpublic void func() { testMapper.updateT1(); t2Service.func(); int i = 1 / 0;}// t2Service@Transactional(propagation = Propagation.NOT_SUPPORTED)public void func() { testMapper.updateT2(); int i = 1 / 0;}
NEVER
很明显,如果存在事务,直接抛出异常
// t1Service@Transactionalpublic void func() { testMapper.updateT1(); t2Service.func();}// t2Service@Transactional(propagation = Propagation.NEVER)public void func() { testMapper.updateT2();}
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:413) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:352) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.14.jar:5.3.14]at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.14.jar:5.3.14]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.14.jar:5.3.14]at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.14.jar:5.3.14]
如果把t1Service
中的事务去掉,则没问题,但t2Service
抛出异常后,也不会回滚
// t1Servicepublic void func() { testMapper.updateT1(); t2Service.func();}// t2Service@Transactional(propagation = Propagation.NEVER)public void func() { testMapper.updateT2(); int i = 1 / 0;}
NESTED
NESTED应该是几种事务传播方式中最难理解的,如果不注意,NESTED和REQUIRED功能看起来则差不多,都可以理解为有事务则加入,没有则新启一个,但实际上NESTED比REQUIRED要更加灵活。
先来看第一个案例,在t1Service中调用t2Service时,对异常进行了捕获,并且也没有抛出。
// t1Service@Transactionalpublic void func() { testMapper.updateT1(); try { t2Service.func(); } catch (Exception e) { e.printStackTrace(); }}// t2Service@Transactional(propagation = Propagation.REQUIRED)public void func() { testMapper.updateT2(); int i = 1 / 0;}
当t2Service
配置为REQUIRED时,t1、t2
都进行了回滚,因为是同一个事务,当如果t2Service
配置为NESTED就不一样了,此时t1
则不会回滚。
// t1Service@Transactionalpublic void func() { testMapper.updateT1(); try { t2Service.func(); } catch (Exception e) { e.printStackTrace(); }}// t2Service@Transactional(propagation = Propagation.NESTED)public void func() { testMapper.updateT2(); int i = 1 / 0;}
NESTED与REQUIRES_NEW的区别
现在有人可能觉得NESTED和REQUIRES_NEW有点相似,但实际上要注意NESTED和REQUIRES_NEW是很有大的区别的
现在我们分别给t1Service
和t2Service
加上一个TransactionSynchronizationManager.getCurrentTransactionName()
输出看看效果
// t1Service@Transactionalpublic void func() { testMapper.updateT1(); System.out.println(TransactionSynchronizationManager.getCurrentTransactionName()); try { t2Service.func(); } catch (Exception e) { e.printStackTrace(); }}// t2Service@Transactional(propagation = Propagation.REQUIRES_NEW)public void func() { testMapper.updateT2(); System.out.println(TransactionSynchronizationManager.getCurrentTransactionName()); int i = 1 / 0;}
输出结果
com.demo.transaction.service.T1Service.funccom.demo.transaction.service.T2Service.func
把REQUIRES_NEW替换为NESTED,可以看出使用NESTED后,实际上还是同一个事务。
com.demo.transaction.service.T1Service.funccom.demo.transaction.service.T1Service.func
NESTED实现方式
这就是NESTED不同之处,两个方法同一个事务,居然没有一起回滚,这就叫嵌套事务,子事务回滚不会影响到主事务,实际上利用的是savepoint
功能,就好像下面这样
-- 主事务savepoint;-- 执行主事务代码-- 子事务savepoint;-- 执行子事务代码-- 子事务提交commit;-- 执行主事务代码-- 主事务提交commit;
所以,如果是在主事务中抛出异常,那么子事务也会被回滚,就像下面这样。
// t1Service@Transactionalpublic void func() { testMapper.updateT1(); t2Service.func(); int i = 1 / 0;}// t2Service@Transactional(propagation = Propagation.NESTED)public void func() { testMapper.updateT2();}