@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有两种CglibAopProxyJdkDynamicAopProxy,其中:

CglibAopProxy在其内部类DynamicAdvisedInterceptorintercept()方法中,判断是否进行事务拦截。

JdkDynamicAopProxy在其invoke()方法中,判断是否进行事务拦截。
这两个方法都会间接调用AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法来获取事务控制的相关属性。这其中有以下一段代码:
@Transactional详解
在这段代码里,方法no-public的都直接返回null了.

详细介绍

@Transactional详解
按这种方式删完数据抛出异常是肯定能回滚的,我们来看另一种,把@Transactional元注解写在被method B上的情况。
@Transactional详解
按照之前的想法,我觉得这样子也是肯定能回滚的,但实际上数据并不会回滚,原因就得从他的实现原理了解起了.

@Transactional事务实现机制

整体事务流程
  1. @Transactional注解的方法被类外部的代码调用时,Spring在运行时为方法所在类生成一个AOP代理对象。
  2. 代理对象根据@Transactional的属性,决定是否由事务拦截器TransactionInterceptor对此方法进行事务拦截。
  3. 在进行事务拦截时,会先开启事务,然后执行业务代码,根据执行是否出现异常,通过抽象事务管理器AbstractPlatformTransactionManager来进行rollback或者commit

上通俗易懂的图解

为什么第一张图有效,第二张图,事务并没有起效呢?看图
@Transactional详解

Spring在检查到@Transactional注解之后,给这个对象生成了一个代理对象proxy
代理对象的methodB,会先开启事务(beginTransaction),然后再去执行原先对象target的methodB,如果抛异常,则回滚(rollBack),如果一切顺利,则提交(commit)。

而最后注入Spring容器的,也正是这个带有事务逻辑的代理对象。所以我们调用methodB时会产生事务。

接下来我们加一个method A看看效果:
@Transactional详解

由于methodA没有加@Transactional注解,所以代理对象里面,直接就是target.methodA(),直接调用了原来对象的methodA。

这下就很清晰了,代理对象的methodA,去调用原来对象的methodA,原来对象的methodA,再去调用原来对象的methodB,而原来对象的methodB,是不具有事务的。事务只存在于代理对象的methodB. 所以整个方法也就没有事务了。