@Transactional详解
Transactional七大事务传播行为
1、TransactionDefinition.PROPAGATION_REQUIRED:
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
2、TransactionDefinition.PROPAGATION_REQUIRES_NEW:
创建一个新的事务,如果当前存在事务,则把当前事务挂起。
3、TransactionDefinition.PROPAGATION_SUPPORTS:
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
4、TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
5、TransactionDefinition.PROPAGATION_NEVER:
以非事务方式运行,如果当前存在事务,则抛出异常。
6、TransactionDefinition.PROPAGATION_MANDATORY:
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
7、TransactionDefinition.PROPAGATION_NESTED:
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;
如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
为什么Transactional必须要用Public修饰呢?
Spring AOP有两种CglibAopProxy
和JdkDynamicAopProxy
,其中:
CglibAopProxy
在其内部类DynamicAdvisedInterceptor
的intercept()
方法中,判断是否进行事务拦截。
JdkDynamicAopProxy
在其invoke()
方法中,判断是否进行事务拦截。
这两个方法都会间接调用AbstractFallbackTransactionAttributeSource
类的computeTransactionAttribute
方法来获取事务控制的相关属性。这其中有以下一段代码:
在这段代码里,方法no-public的都直接返回null了.
详细介绍
按这种方式删完数据抛出异常是肯定能回滚的,我们来看另一种,把@Transactional
元注解写在被method B上的情况。
按照之前的想法,我觉得这样子也是肯定能回滚的,但实际上数据并不会回滚,原因就得从他的实现原理了解起了.
@Transactional事务实现机制
整体事务流程
- 当
@Transactional
注解的方法被类外部的代码调用时,Spring在运行时为方法所在类生成一个AOP代理对象。 - 代理对象根据
@Transactional
的属性,决定是否由事务拦截器TransactionInterceptor对此方法进行事务拦截。 - 在进行事务拦截时,会先开启事务,然后执行业务代码,根据执行是否出现异常,通过抽象事务管理器
AbstractPlatformTransactionManager
来进行rollback
或者commit
。
上通俗易懂的图解
为什么第一张图有效,第二张图,事务并没有起效呢?看图
Spring在检查到@Transactional
注解之后,给这个对象生成了一个代理对象proxy
代理对象的methodB,会先开启事务(beginTransaction),然后再去执行原先对象target的methodB,如果抛异常,则回滚(rollBack),如果一切顺利,则提交(commit)。
而最后注入Spring容器的,也正是这个带有事务逻辑的代理对象。所以我们调用methodB时会产生事务。
接下来我们加一个method A看看效果:
由于methodA没有加@Transactional注解,所以代理对象里面,直接就是target.methodA(),直接调用了原来对象的methodA。
这下就很清晰了,代理对象的methodA,去调用原来对象的methodA,原来对象的methodA,再去调用原来对象的methodB,而原来对象的methodB,是不具有事务的。事务只存在于代理对象的methodB. 所以整个方法也就没有事务了。