Spring源码解析(十二):TransactionInterceptor事务拦截器

news/2024/11/24 9:31:01/

Spring源码系列文章

Spring源码解析(一):环境搭建

Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean

Spring源码解析(三):bean容器的刷新

Spring源码解析(四):单例bean的创建流程

Spring源码解析(五):循环依赖

Spring源码解析(六):bean工厂后置处理器ConfigurationClassPostProcessor

Spring源码解析(七):bean后置处理器AutowiredAnnotationBeanPostProcessor

Spring源码解析(八):bean后置处理器CommonAnnotationBeanPostProcessor

Spring源码解析(九):AOP源码之@Aspect所有相关注解解析

Spring源码解析(十):spring整合mybatis源码

Spring源码解析(十一):spring事务配置类源码

Spring源码解析(十二):TransactionInterceptor事务拦截器


目录

  • TransactionInterceptor事务拦截器类图
  • 一、获取事务属性 TransactionAttribute
  • 二、创建事务信息 TransactionInfo
    • 1、tm.getTransaction 通过事务管理器创建事务
    • TransactionSynchronizationManager事务同步管理器
    • 直接调用(当前不存在事务)
      • 1)doGetTransaction获取事务连接对象
      • 2)isExistingTransaction判断是否存在事务
      • 3)startTransaction开启事务(required、requires_new、nested)
      • 4)开启空事务(supports、not_supported、never)
      • 嵌套调用(已经存在事务)
        • 1)suspend挂起外围事务
    • 2、prepareTransactionInfo 将事务信息绑定到当前线程
  • 三、执行拦截器链的下一个拦截器 invocation.proceedWithInvocation();
  • 四、执行出现异常 completeTransactionAfterThrowing
    • 1、触发事务同步的beforeCompletion回调方法
    • 2、rollbackToHeldSavepoint 回滚保存点
    • 3、doRollback 回滚事务
    • 4、doSetRollbackOnly 设置仅回滚
    • 5、触发事务同步的afterCompletion回调方法
    • 6、cleanupAfterCompletion 完成后清理
      • 6.1、resume 恢复事务
  • 五、清理事务信息 cleanupTransactionInfo
  • 六、提交事务 commitTransactionAfterReturning
  • 总结

TransactionInterceptor事务拦截器类图

在这里插入图片描述

  • TransactionInterceptor实现了MethodInterceptor,核心方法就是invoke方法
// invocation:代表了要进行事务管理的方法
public Object invoke(MethodInvocation invocation) throws Throwable {Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// 核心方法就是invokeWithinTransactionreturn invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

TransactionAspectSupport#invokeWithinTransaction方法分析

  • 主要可以分为三段:响应式事务管理标准事务管理通过回调实现事务管理
  • 这里我们只分析标准事务管理
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {// 之前在配置类中注册了一个AnnotationTransactionAttributeSource// 这里就是直接返回了之前注册的那个Bean,通过它去获取事务属性// 创建事务拦截器时候就把AnnotationTransactionAttributeSource set进来了,这里直接获取TransactionAttributeSource tas = getTransactionAttributeSource();// 解析@Transactional注解获取事务属性final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);// 获取对应的事务管理器// 可以做在注解属性transactionManager中指定事务管理器// 配置类@Bean new DataSourceTransactionManager();final TransactionManager tm = determineTransactionManager(txAttr);// ...// 忽略响应式的事务管理// ... // 做了个强转PlatformTransactionManager// 这里是DataSourceTransactionManager转为PlatformTransactionManagerPlatformTransactionManager ptm = asPlatformTransactionManager(tm);// 切点名称(类名+方法名),会被作为事务的名称final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {		  // 创建事务TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {// 这里执行真正的业务逻辑retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 方法执行出现异常,在异常情况下完成事务completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {// 清除线程中的事务信息cleanupTransactionInfo(txInfo);}// ...// 省略不重要代码// ...// 提交事务commitTransactionAfterReturning(txInfo);return retVal;}// ....// 省略回调实现事务管理相关代码// ....return result;}
}

总结:

  1. 获取事务属性:tas.getTransactionAttribute
  2. 创建事务:createTransactionIfNecessary
  3. 执行业务逻辑:invocation.proceedWithInvocation
  4. 异常时完成事务:completeTransactionAfterThrowing
  5. 清除线程中绑定的事务信息:cleanupTransactionInfo
  6. 提交事务:commitTransactionAfterReturning

一、获取事务属性 TransactionAttribute

// getTransactionAttribute:先从拦截的方法上找@Transactional注解
// 如果方法上没有的话,再从方法所在的类上找,如果类上还没有的话尝试从接口或者父类上找
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}// 在缓存中查找Object cacheKey = getCacheKey(method, targetClass);TransactionAttribute cached = this.attributeCache.get(cacheKey);if (cached != null) {if (cached == NULL_TRANSACTION_ATTRIBUTE) {return null;}else {return cached;}}else {// 这里真正的去执行解析TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);// 缓存解析的结果,如果为事务属性为null,也放入一个标志// 代表这个方法不需要进行事务管理if (txAttr == null) {// NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);}else {String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);if (txAttr instanceof DefaultTransactionAttribute) {((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);}this.attributeCache.put(cacheKey, txAttr);}return txAttr;}
}

真正解析注解时调用了computeTransactionAttribute方法

  • 默认情况下allowPublicMethodsOnly为true,如果方法不是public,属性返回空,那么@Transactional不生效
  • 查找类方法@Transactional注解属性的顺序
    1. 先在方法上找(优先级最高
    2. 再到方法所在类上找
    3. 最后到重写的方法和类或实现方法接口的方法和类上找(一般不会走到这步)
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {// 默认情况下allowPublicMethodsOnly为true// 这意味着@Transactional如果放在非public方法上不会生效if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// method是接口中的方法// specificMethod是具体实现类的方法Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);// 先在目标类方法上找TransactionAttribute txAttr = findTransactionAttribute(specificMethod);if (txAttr != null) {return txAttr;}// 再在目标类上找txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}// 到这里表示没有在目标方法或者类上找到事务注解,如果最终的目标方法和当前方法不一致,那么在当前方法中查找// 实际上很难走到这一步,在此前的查找中基本上就返回了// 最后到接口跟接口中的方法上找这个注解if (specificMethod != method) {txAttr = findTransactionAttribute(method);if (txAttr != null) {return txAttr;}txAttr = findTransactionAttribute(method.getDeclaringClass());if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {return txAttr;}}//以上都找不到事务注解,那么将返回null,表示当前方法不会进行事务代理return null;
}
  • 查询@Transactional注解的的属性,并包装成RuleBasedTransactionAttribute对象返回

在这里插入图片描述

RuleBasedTransactionAttribute类图:

在这里插入图片描述

获取对应的事务管理器

  • 可以做在@Transactional注解属性transactionManager中指定事务管理器
  • transactionManager没有指定,则寻找配置类@Bean new DataSourceTransactionManager()添加的bean
@Nullable
protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {// 如果没有事务属性,那么直接获取手动设置的事务管理器,可能返回null//编程式事务也会调用该方法,并且没有事务属性if (txAttr == null || this.beanFactory == null) {return getTransactionManager();}//获取限定符,也就是每一个@Transactional注解的value属性或者transactionManager属性String qualifier = txAttr.getQualifier();//如果设置了该属性,那么通过限定符从容器中查找指定的事务管理器//一般都是注解设置的,基于XML的<tx:method/>标签没有配置该属性if (StringUtils.hasText(qualifier)) {return determineQualifiedTransactionManager(this.beanFactory, qualifier);}//否则,如果设置了transactionManagerBeanName属性,那么通过限定符查找指定的事务管理器//一般都是基于XML的<tx:annotation-driven/>标签的transaction-manager属性else if (StringUtils.hasText(this.transactionManagerBeanName)) {return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);}//否则,将会查找事务管理器,这个就是现在最常见的逻辑else {//获取手动设置的事务管理器TransactionManager defaultTransactionManager = getTransactionManager();//如果没有,那么获取默认的事务管理器if (defaultTransactionManager == null) {//从缓存中通过默认key尝试获取默认的事务管理器defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);//如果为nullif (defaultTransactionManager == null) {//那么尝试从容器中查找TransactionManager类型的bean,并且作为默认事务管理器//这就是基于注解配置的最常见的逻辑,该方法如果找不到事务管理器或者找到多个事务管理器,那么将抛出异常defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);//找到了之后存入缓存,key就是默认keythis.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);}}//返回事务管理器return defaultTransactionManager;}
}

二、创建事务信息 TransactionInfo

  • 创建事务实际上就是创建了一个TransactionInfo
  • TransactionInfo对象包含了事务相关的所有信息
    • 实现事务使用的事务管理器(PlatformTransactionManager)
    • 事务的属性(TransactionAttribute)
    • 事务的状态(TransactionStatus)
    • 与当前创建的关联的上一个事务信息(oldTransactionInfo)
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {// 如果没有为事务指定名称,使用切点作为事务名称("com.xc.aop.Calculatorlmpl.add")if (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}TransactionStatus status = null;if (txAttr != null) {if (tm != null) {// 调用事务管理器的方法,获取一个事务并返回事务的状态status = tm.getTransaction(txAttr);}// ....省略日志}// 将事务相关信息封装到TransactionInfo对象中// 并将TransactionInfo绑定到当前线程return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

1、tm.getTransaction 通过事务管理器创建事务

  • 可以分为两种情况分析
    • 应用程序直接调用了一个被事务管理的方法(直接调用
    • 在一个需要事务管理的方法中调用了另外一个需要事务管理的方法(嵌套调用
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException {// 事务的属性(TransactionAttribute),通过解析@Transacational注解的属性及一些默认属性TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());// 获取一个数据库事务对象(DataSourceTransactionObject),// 这个对象中封装了一个从当前线程上下文中获取到的连接 Object transaction = doGetTransaction();boolean debugEnabled = logger.isDebugEnabled();// 判断是否存在事务// 如果之前获取到的连接不为空,并且连接上激活了事务,那么就为trueif (isExistingTransaction(transaction)) {// 如果已经存在了事务,需要根据不同传播机制进行不同的处理return handleExistingTransaction(def, transaction, debugEnabled);}/** 到这里表示没有已存在的事务,进入第一个事务方法时将会走这个逻辑*/// 设置的事务超时时间如果小于默认超时时间(-1),将会抛出异常:无效的超时时间if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());}// 如果配置的事务的传播行为是mandatory,直接抛出异常// 该传播行为的含义是:必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}// 否则,如果配置的事务的传播行为是required或者requires_new或者nested// 这几个传播行为的含义的共同点之一就是:如果当前不存在事务,就创建一个新事务运行// 那么这里将开启一个新事物else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {// 如果存在同步,将注册的同步挂起,获取挂起的资源,这里为空// 挂起操作,但是此时没事务也没激活同步,反正就是啥也没干SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);}try {// 开启一个新事务return startTransaction(def, transaction, debugEnabled, suspendedResources);}catch (RuntimeException | Error ex) {// 唤醒此前挂起的事务和资源resume(null, suspendedResources);throw ex;}}// 否则,配置的事务的传播行为就是剩下的三种:supports或never或not_supported// 这几个传播行为的含义的共同点之一就是:当前方法一定以非事务的方式运行else {// 创建一个空事务,没有实际的事务提交以及回滚机制// 会激活同步:将数据库连接绑定到当前线程上boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);}
}

TransactionSynchronizationManager事务同步管理器

  • 主要用于管理每一个线程当前所使用的数据库事务连接资源事务同步器(TransactionSynchronization)
  • 一个线程当前只能激活一个连接资源,因此如果需要绑定新的连接资源,那么需要将此前绑定的资源删除(或者说保存起来,等新资源使用完毕之后再恢复此前的连接资源)
  • 为了保证事务所有的SQL都能够使用一个数据库连接,这个时候我们需要将数据库连接跟事务进行同步
  • 事务同步必须由事务管理器通过initSynchronization()和clearSynchronization()来进行激活和停用,使用isSynchronizationActive()检测当前是否具有事务同步
  • TransactionSynchronizationManager内部是通过很多ThreadLocal(线程本地变量)类型的属性来实现为当前线程维护自己的资源的功能的,实现资源隔离
/*** 事务资源管理器*/
public abstract class TransactionSynchronizationManager {/*** 事务资源* 一个ThreadLocal属性,用于存放线程当前使用的数据库资源* value是一个Map<Object, Object>,key为某个数据源DataSource ,value实际上就是连接ConnectionHolder*/private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** 事务同步* 一个ThreadLocal属性,用于存放线程当前激活的事务同步器TransactionSynchronization* 每个线程都可以开启多个事物同步,用于在处理事务的各个阶段进行自定义扩展或者回调* TransactionSynchronization的同步回调功能类似于此前学习的@TransactionalEventListener*/private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");/*** 当前事务的名称* 一个ThreadLocal属性,用于存放线程当前的事务的名称*/private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<>("Current transaction name");/*** 当前事务的只读状态* 一个ThreadLocal属性,用于存放线程当前的事务的只读状态*/private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<>("Current transaction read-only status");/*** 当前事务的隔离级别* 一个ThreadLocal属性,用于存放线程当前的当前事务的隔离级别*/private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<>("Current transaction isolation level");/*** 当前事务是否开启* 一个ThreadLocal属性,用于存放线程当前是否开启了事务*/private static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<>("Actual transaction active");//与事务资源相关的管理方法//………………}

TransactionSynchronization事务同步

  • 该类可以记录事务的执行状态,并且可以在某个状态转变时执行各种回调操作
public interface TransactionSynchronization extends Flushable {/** 事务提交状态 */int STATUS_COMMITTED = 0;/** 事务回滚状态 */int STATUS_ROLLED_BACK = 1;/**系统异常状态 */int STATUS_UNKNOWN = 2;//在spring开启新事务,获取connection之前会调用(未执行registCustomer)void suspend();//开启新事务失败时会调用(未执行registCustomer)void resume();//没调用void flush();// 事务提交之前void beforeCommit(boolean readOnly);// 事务成功或者事务回滚之前void beforeCompletion();// 事务成功提交之后void afterCommit();// 操作完成之后(包含事务成功或者事务回滚)void afterCompletion(int status);
}

直接调用(当前不存在事务)

流程图:

在这里插入图片描述

1)doGetTransaction获取事务连接对象

  • DataSourceTransactionObject对象,内部持有ConnectionHolder(数据连接持有对象)
  • obtainDataSource():获取数据源对象,数据库url用户密码,配置类对象DriverManagerDataSource
  • TransactionSynchronizationManager.getResource:从当前线程ThreadLocal<Map<Object, Object>> resources获取连接持有对象ConnectionHolder,因为此时还没有获取连接及绑定到当前线程,所以ConnectionHolder对象为空
@Override
protected Object doGetTransaction() {//创建一个DataSourceTransactionObject,由DataSourceTransactionManager用作内部事务对象//内部可能会持有一个ConnectionHolder对象,还具有创建、回滚、释放保存点的功能DataSourceTransactionObject txObject = new DataSourceTransactionObject();//设置是否允许保存点,DataSourceTransactionManager默认会允许,用于实现嵌套事务txObject.setSavepointAllowed(isNestedTransactionAllowed());//obtainDataSource方法用于获取配置的数据源,就是我们自己配置的数据源DriverManagerDataSource//getResource用于获取此线程在当前数据源中已拥有JDBC连接资源持有者ConnectionHolder//如果此前没有获取过连接,则返回null;如果此前开启了过事务(外层事务),那么肯定不会获取nullConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());//设置连接信息,newConnectionHolder属性设置为false,这表示默认此前已经存在ConnectionHolder//但实际上可能并没有,因此后面的步骤中会再次判断该值txObject.setConnectionHolder(conHolder, false);return txObject;
}

2)isExistingTransaction判断是否存在事务

  • 上一步说到,连接为空,那么此方法返回为false,证明是直接调用
  • 否则有事务且激活则是嵌套调用,单独的一套流程据不同传播机制进行不同的处理
// 检查是否有现有事务(即已经开启过事务)
protected boolean isExistingTransaction(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// 只有在存在连接并且连接上已经激活了事务才会返回truereturn (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}

3)startTransaction开启事务(required、requires_new、nested)

  • 事务的传播行为是required或者requires_new或者nested则开启事务
  • 新建一个DefaultTransactionStatus实现并返回,内部持有我们为当前方法配置的事务属性或者默认属性,以及保存着此前挂起的资源
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {//判断是否需要开启新同步,默认都是SYNCHRONIZATION_ALWAYS,即需要开启boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 根据之前的事务定义等相关信息构造一个事务状态对象DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 真正开启事务,会从数据源中获取连接并绑定到线程上doBegin(transaction, definition);// 在这里会激活同步prepareSynchronization(status, definition);return status;
}/*** AbstractPlatformTransactionManager的方法* 为给定参数新创建一个TransactionStatus实例,实际类型为DefaultTransactionStatus** @param definition         为当前方法配置的事务定义* @param transaction        获取的事务对象* @param newTransaction     是否是新事物* @param newSynchronization 是否开启事务同步* @param debug              是否支持debug级别的日志* @param suspendedResources 被挂起的资源,比如此前的事务同步* @return DefaultTransactionStatus对象*/
protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {//如果newSynchronization为true并且当前线程没有绑定的事务同步,那么确定开启新事物同步//由于此前调用了suspend方法清理了此前的事务同步,因此一般都是需要开启新事务同步,即为trueboolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();//返回一个新建的DefaultTransactionStatus对象,该对象被用来表示新开启的事务,是TransactionStatus的默认实现//内部包括了各种新开启的事务状态,当然包括此前挂起的事务的资源return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,definition.isReadOnly(), debug, suspendedResources);
}

doBegin真正开启事务

  • 获取数据库连接Connection
  • 设置数据库事务的隔离级别只读标志属性
  • 设置事务手动提交,修改为autoCommit=false
  • 数据库连接绑定到当前线程上,即ThreadLocal<Map<Object, Object>> resources
protected void doBegin(Object transaction, TransactionDefinition definition) {// 强转为内部事务对象DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {// 判断txObject中是否存在连接并且连接上已经激活了事务// txObject是通过之前的doGetTransaction方法得到的// 直接调用的情况下,这个判断肯定为trueif (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {// 从数据源中获取一个连接Connection newCon = obtainDataSource().getConnection();// 将连接放入到txObject中// 第二个参数为true,标志这是一个新连接txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}// 获取事务对象的连接持有者,将synchronizedWithTransaction设置为true,即资源标记为与事务同步txObject.getConnectionHolder().setSynchronizedWithTransaction(true);// 获取内部保存的连接con = txObject.getConnectionHolder().getConnection();// 设置数据库事务的隔离级别,只读标志属性(下面细说)Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);txObject.setReadOnly(definition.isReadOnly());// 一般数据源默认自动提交,这会会修改为autoCommit=false,手动提交if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);con.setAutoCommit(false);}// 只读事务优化(下面细说)prepareTransactionalConnection(con, definition);// 设置事务ConnectionHolder的transactionActive属性为true,表示激活当前连接的事务// 此前判断是否有开启事务的isExistingTransaction方法就会判断这个属性txObject.getConnectionHolder().setTransactionActive(true);// 设置超时时间int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// 如果是新的连接持有者,即newConnectionHolder属性为trueif (txObject.isNewConnectionHolder()) {// 将连接绑定到当前线程上// 绑定ConnectionHolder资源到TransactionSynchronizationManager的resources属性中// key就是当前的属性源,value就是ConnectionHolderTransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {// 出现异常的话,需要归还连接if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, obtainDataSource());txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}
}

prepareConnectionForTransaction 设置隔离级别和只读属性:

  • 设置数据库连接的只读属性
  • 如果手动设置的隔离级别不等于当前连接的隔离级别,修改连接的隔离级别,并保存以前的的隔离级别
@Nullable
public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)throws SQLException {boolean debugEnabled = logger.isDebugEnabled();// 设置只读标志。if (definition != null && definition.isReadOnly()) {try {//设置连接只读属性con.setReadOnly(true);} catch (SQLException | RuntimeException ex) {Throwable exToCheck = ex;while (exToCheck != null) {if (exToCheck.getClass().getSimpleName().contains("Timeout")) {throw ex;}exToCheck = exToCheck.getCause();}}}// 以前的隔离级别Integer previousIsolationLevel = null;//如果存在隔离级别并且不等于默认配置,即不等于ISOLATION_DEFAULT(该级别的意思是使用数据库的默认级别)if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {//获取当前连接的隔离级别int currentIsolation = con.getTransactionIsolation();// 如果手动设置的隔离级别不等于连接的隔离级别if (currentIsolation != definition.getIsolationLevel()) {//记录连接的隔离级别previousIsolationLevel = currentIsolation;//连接的隔离级别手动设置为我们配置的隔离级别con.setTransactionIsolation(definition.getIsolationLevel());}}//返回此前的连接的隔离级别,可能为nullreturn previousIsolationLevel;
}

prepareTransactionalConnection优化只读事务:

  • 只读事务:查询语句将不会查询到该事务期间提交的内容,只能查询到事务开始之前提交的内容
  • mysql支持Spring的readOnly参数,即支持JDBC的con.setReadOnly(true),因此就没必要再设置enforceReadOnly为true
  • oracle不支持con.setReadOnly(true),Spring的readOnly配置对于Oracle无效,需要通过stmt.executeUpdate(“SET TRANSACTION READ ONLY”);实现只读事务
protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)throws SQLException {//如果将"enforceReadOnly"标志设置为true,并且事务定义指示只读事务if (isEnforceReadOnly() && definition.isReadOnly()) {//那么获取Statement,并且执行"SET TRANSACTION READ ONLY"sql语句try (Statement stmt = con.createStatement()) {stmt.executeUpdate("SET TRANSACTION READ ONLY");}}
}/*** DataSourceTransactionManager的属性* 表示是否通过对事务连接显式执行sql语句强制执行事务的只读性质,默认为false*/
private boolean enforceReadOnly = false;

bindResource绑定资源到resources:

  • 对于新获取的连接资源会被绑定到TransactionSynchronizationManagerresources线程本地变量属性中
  • key就是当前的属性源DataSource(DriverManagerDataSource),value就是ConnectionHolder
/*** 事务资源*/
private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** TransactionSynchronizationManager的方法* 将给定key的给定资源value绑定到当前线程。* 对于DataSourceTransactionManager,key就是DataSource实例,value就是ConnectionHolder*/
public static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");//获取当前线程的本地资源mapMap<Object, Object> map = resources.get();//如果找不到,则设置一个Mapif (map == null) {map = new HashMap<>();resources.set(map);}//将actualKey和value存入map中,返回旧的valueObject oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}//如果已经有绑定到线程的当前key的值,则抛出异常if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}if (logger.isTraceEnabled()) {logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +Thread.currentThread().getName() + "]");}
}

prepareSynchronization准备事务同步

  • doBegin开启新事物之后,用于准备事务同步
  • 就是将当前事务属性绑定到TransactionSynchronizationManager的对应的线程本地变量中
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {//是否是新同步,在真正的开启新事务的时候,一般都是trueif (status.isNewSynchronization()) {// 到这里真正激活了事务TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());// 隔离级别// 只有在不是默认隔离级别的情况下才会绑定到线程上,否则绑定一个nullTransactionSynchronizationManager.setCurrentTransactionIsolationLevel(definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?definition.getIsolationLevel() : null);// 只读状态TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());// 事务名称TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());// 初始化同步-ThreadLocal设置一个空LinkedHashSetTransactionSynchronizationManager.initSynchronization();}
}private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");/*** TransactionSynchronizationManager的方法* 激活当前线程的事务同步。由事务管理器在事务开始时调用。* 初始化就是设置一个空LinkedHashSet*/
public static void initSynchronization() throws IllegalStateException {// synchronizations绑定内容不为空,isSynchronizationActive()返回true// 如果同步已处于活动状态,即synchronizations保存的线程本地变量不为null,则抛出异常if (isSynchronizationActive()) {throw new IllegalStateException("Cannot activate transaction synchronization - already active");}logger.trace("Initializing transaction synchronization");//否则就为当前初始化一个线程本地变量,这是一个空的LinkedHashSet//虽然没有任何的TransactionSynchronization,但是已经不为null了synchronizations.set(new LinkedHashSet<>());
}

4)开启空事务(supports、not_supported、never)

  • 新建一个DefaultTransactionStatus事务状态对象返回
  • 与startTransaction方法比较,没有调用doBegin方法,这意味这个这个方法不会去获取数据库连接,更不会绑定数据库连接到上下文中,实际没有开启事务,仅仅是做了一个同步的初始化
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException {// ......// mandatory抛出异常// required、requires_new、nested调用startTransaction开启事务// supports、not_supported、never会进入下面这个判断else {// 默认同步级别就是alwaysboolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);// 直接调用prepareTransactionStatus返回一个事务状态对象return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);}
}// prepareTransactionStatus方法
protected final DefaultTransactionStatus prepareTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {// 调用这个方法时// definition:传入的是解析@Transactional注解得到的事务属性// transaction:写死的传入为null,意味着没有真正的事务()// newTransaction:写死的传入为true// newSynchronization:默认同步级别为always,在没有真正事务的时候也进行同步// suspendedResources:写死了,传入为null,不挂起任何资源DefaultTransactionStatus status = newTransactionStatus(definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);prepareSynchronization(status, definition);return status;
}
  • 到这里,直接调用事务开启完毕,创建TransactionStatus事务状态对象完成

总结直接调用情况下

  • 传播级别为mandatory会直接抛出异常
  • 传播级别为requiredrequires_newnested时,会调用startTransaction真正开启一个事务
  • 传播级别为supportsnot_supportednever时,会开启一个空事务

嵌套调用(已经存在事务)

流程图:

在这里插入图片描述

  • 从新进入获取事务状态对象的方法,此时DataSourceTransactionObject对象的数据库连接不再为空
  • 只有外围的事务的传播级别为requiredrequires_newnested时,isExistingTransaction才会成立,因为只有在这些传播级别下才会真正的开启事务,才会将连接绑定到当前线程
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException {// 事务的属性(TransactionAttribute)TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());// 从线程上下文中获取到一个连接,并封装到一个DataSourceTransactionObject对象中Object transaction = doGetTransaction();boolean debugEnabled = logger.isDebugEnabled();// 判断之前获取到的事务上是否有连接,并且连接上激活了事务if (isExistingTransaction(transaction)) // 嵌套调用处理在这里return handleExistingTransaction(def, transaction, debugEnabled);}// ...// 下面是直接调用的情况,前文已经分析过了// 省略直接调用的相关代码
}protected boolean isExistingTransaction(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// 首先判断txObject中是否已经绑定了连接// 其次判断这个连接上是否已经激活了事务return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}

处理嵌套事务 handleExistingTransaction

  • 如果嵌套的事务传播级别为never,那么直接抛出异常
  • 如果嵌套的事务传播级别为not_soupported,那么挂起外围事务,开启一个新的空事务
  • 如果嵌套的事务传播级别为requires_new,那么挂起外围事务,并且新建一个新的事务
    • 新事务和外围事务互不影响,有自己的提交和回滚机制,毕竟是两个不同数据库连接
  • 如果嵌套的事务传播级别为nested,会获取当前线程绑定的数据库连接创建一个保存点
    • 它没有挂起任何事务相关的资源,仅仅是创建了一个保存点而已
    • 这个事务在回滚时,只会回滚到指定的保存点,不影响外围事务
    • 同时因为它跟外围事务共用一个连接,所以外层事务回滚嵌套事务也会一起回滚
  • 如果嵌套的事务传播级别为supports、required、mandatory时,校验嵌套事务的属性
    • 加入到外围事务中,也就是使用外围事务,也就不是新事务,不是新的同步
private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {// 如果嵌套的事务的传播级别为never,那么直接抛出异常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");}// 如果嵌套的事务的传播级别为not_soupported,那么挂起外围事务if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {if (debugEnabled) {logger.debug("Suspending current transaction");}// 挂起外围事务Object suspendedResources = suspend(transaction);boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);// 开启一个新的空事务return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);}// 如果嵌套的事务传播级别为requires_new,那么挂起外围事务,并且新建一个新的事务if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {SuspendedResourcesHolder suspendedResources = suspend(transaction);try {return startTransaction(definition, transaction, debugEnabled, suspendedResources);}catch (RuntimeException | Error beginEx) {resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}}// 如果嵌套事务的传播级别为nested,会获取当前线程绑定的数据库连接// 并通过数据库连接创建一个保存点(save point)// 其实就是调用Connection的setSavepoint方法if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {// 默认是允许的,所以这个判断不会成立if (!isNestedTransactionAllowed()) {throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - " +"specify 'nestedTransactionAllowed' property with value 'true'");}// 默认是trueif (useSavepointForNestedTransaction()) {DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);status.createAndHoldSavepoint();return status;}else {// JTA进行事务管理才会进入这这里,我们不做考虑return startTransaction(definition, transaction, debugEnabled, null);}}if (debugEnabled) {logger.debug("Participating in existing transaction");}// 嵌套事务传播级别为supports、required、mandatory时,是否需要校验嵌套事务的属性// 主要校验的是个隔离级别跟只读属性// 默认是不需要校验的// 如果开启了校验,那么会判断如果外围事务的隔离级别跟嵌套事务的隔离级别是否一致// 如果不一致,直接抛出异常if (isValidateExistingTransaction()) {if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {Constants isoConstants = DefaultTransactionDefinition.constants;// 这里会抛出异常}}// 嵌套事务的只读为falseif (!definition.isReadOnly()) {// 但是外围事务的只读为true,那么直接抛出异常if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {// 这里会抛出异常}}}boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
1)suspend挂起外围事务
  • 清空外围事务绑定在线程上的同步
  • 挂起是因为将来还要恢复,所以不能单纯的只是清空呀,还得将清空的信息保存到当前的事务上,这样当当前的事务完成后可以恢复到挂起时的状态,以便于外围的事务能够正确的运行下去
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {// 如果当前线程的事务同步处于活动状态,即存在绑定的TransactionSynchronization,则返回true。// 嵌套调用下,同步是已经被激活了的if (TransactionSynchronizationManager.isSynchronizationActive()) {// 解绑线程上绑定的同步回调,并返回List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();try {Object suspendedResources = null;if (transaction != null) {// 这里实际上就是解绑线程上绑定的数据库连接// 同时返回这个连接suspendedResources = doSuspend(transaction);}// 解绑线程上绑定的事务属性// 获取并清空(设置为null)事物名称String name = TransactionSynchronizationManager.getCurrentTransactionName();TransactionSynchronizationManager.setCurrentTransactionName(null);// 获取并清空(设置为false)事物只读状态boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);// 获取并清空(设置为null)事物隔离级别Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);// 获取并清空(设置为false)事物是否激活boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();TransactionSynchronizationManager.setActualTransactionActive(false);// 最后集中封装为一个SuspendedResourcesHolder返回// 之后再commit后或者rollback后使用return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);}catch (RuntimeException | Error ex) {// 出现异常的话,恢复doResumeSynchronization(suspendedSynchronizations);throw ex;}}// 如果没有事务同步但是开启了事务,那么挂起事务// 不知道什么情况会进到这里else if (transaction != null) {// 如果没有激活同步,那么也需要将连接挂起Object suspendedResources = doSuspend(transaction);// 将挂起的资源存入一个SuspendedResourcesHolder对象中返回return new SuspendedResourcesHolder(suspendedResources);}else {// 事务或者事务同步均未激活,返回null,什么也不干return null;}
}

doSuspendSynchronization挂起事务同步

/*** AbstractPlatformTransactionManager的方法* 挂起所有当前同步,并停用当前线程的事务同步。** @return 挂起的TransactionSynchronization对象的列表*/
private List<TransactionSynchronization> doSuspendSynchronization() {//获取线程的当前的所有事务同步列表List<TransactionSynchronization> suspendedSynchronizations =TransactionSynchronizationManager.getSynchronizations();//遍历,依次挂起每一个事务同步for (TransactionSynchronization synchronization : suspendedSynchronizations) {synchronization.suspend();}//清除synchronizations属性中保存的的当前线程的当前事务同步集合的引用TransactionSynchronizationManager.clearSynchronization();//返回被挂起的事务同步return suspendedSynchronizations;
}/*** TransactionSynchronizationManager的方法* 调用该方法时一定要保证当前线程存在事务同步,否则将抛出异常* 因此需要先调用isSynchronizationActive方法来校验* 返回当前线程的所有已注册的事务同步的无法修改的快照列表*/
public static List<TransactionSynchronization> getSynchronizations() throws IllegalStateException {// 获取当前线程的事务同步列表Set<TransactionSynchronization> synchs = synchronizations.get();// 为null就抛出IllegalStateException异常if (synchs == null) {throw new IllegalStateException("Transaction synchronization is not active");}// 返回不可修改的快照,以避免在迭代和调用可能进一步注册同步的同步回调时抛出ConcurrentModificationExceptions。if (synchs.isEmpty()) {return Collections.emptyList();} else {//在获取的之后对快照进行排序List<TransactionSynchronization> sortedSynchs = new ArrayList<>(synchs);AnnotationAwareOrderComparator.sort(sortedSynchs);return Collections.unmodifiableList(sortedSynchs);}
}/*** TransactionSynchronizationManager的方法* 调用该方法时一定要保证当前线程存在事务同步,否则将抛出异常* 因此需要先调用isSynchronizationActive方法来校验* 停用当前线程的事务同步,由事务管理器在事务清理中调用** @throws IllegalStateException 如果同步未激活*/
public static void clearSynchronization() throws IllegalStateException {// 如果没有激活事务同步,同样抛出异常if (!isSynchronizationActive()) {throw new IllegalStateException("Cannot deactivate transaction synchronization - not active");}logger.trace("Clearing transaction synchronization");// 移除当前线程绑定到synchronizations属性的值synchronizations.remove();
}

2、prepareTransactionInfo 将事务信息绑定到当前线程

  • 根据事务属性、事务管理器、方法连接点描述字符串(全限定方法名)信息创建一个事务信息对象TransactionInfo
  • 保存旧事务信息,并将新事务信息TransactionInfo绑定到当前线程ThreadLocal<TransactionInfo> transactionInfoHolder
/*** TransactionAspectSupport的方法* 为给定的属性和状态对象准备一个TransactionInfo** @param txAttr                  TransactionAttribute,可能为null* @param joinpointIdentification 完全限定的方法名称,用于记录日志* @param status                  当前事务的TransactionStat*/
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, String joinpointIdentification,@Nullable TransactionStatus status) {/** 根据给定的事务属性、事务管理器、方法连接点描述字符串(全限定方法名)信息创建一个事务信息对象TransactionInfo*/TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);// 如果事务属性不为nullif (txAttr != null) {//那么设置事务状态,这里就表示为当前方法创建了一个事务txInfo.newTransactionStatus(status);}else {}//将TransactionInfo绑定到当前线程txInfo.bindToThread();return txInfo;
}// 将旧的事务信息赋值,以后恢复时候使用
private TransactionInfo oldTransactionInfo;// 将新事务信息绑定当前线程的ThreadLocal
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =new NamedThreadLocal<>("Current aspect-driven transaction");private void bindToThread() {// Expose current TransactionStatus, preserving any existing TransactionStatus// for restoration after this transaction is complete.this.oldTransactionInfo = transactionInfoHolder.get();transactionInfoHolder.set(this);
}

三、执行拦截器链的下一个拦截器 invocation.proceedWithInvocation();

  • 当前执行内容还在事务拦截器TransactionInterceptor的invoke方法中
  • 此时事务状态已经开启,则去执行真正的逻辑
  • 如果有其他环绕前置等拦截器,则会进入,再由其他拦截器入进入真正的代码逻辑

在这里插入图片描述

  • spring集成mybatis的代理invoke方法

在这里插入图片描述

  • 获取SqlSession,并将其绑定到当前线程ThreadLocal的resources变量
  • 添加事务同步对象到当前线程ThreadLoacl的synchronizations变量,事务的状态,挂起、回调方法等会用到事务同步对象

在这里插入图片描述
在这里插入图片描述

  • 执行sql时候获取数据库连接Connection,当初创建事务放入本地线程Threadlocal的连接

在这里插入图片描述

四、执行出现异常 completeTransactionAfterThrowing

  • 出现异常时其实分为两种情况,并不是一定会回滚
  • 只有在满足txInfo.transactionAttribute.rollbackOn(ex)这个条件时才会真正执行回滚,否则即使出现了异常也会先提交事务
  • 这个条件取决于@Transactiona注解中的rollbackFor属性是如何配置的,如果不进行配置的话,默认只会对RuntimeException或者Error进行回滚
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {// transactionAttribute是从@Transactional注解中解析得来的if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {try {// 回滚txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}// 省略异常处理}else {try {// 即使出现异常仍然提交事务txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}// 省略异常处理}}
}

在进行回滚时会调用事务管理器的rollback方法

public final void rollback(TransactionStatus status) throws TransactionException {// 如果当前事务已完成,即已提交或者回滚,那么抛出异常if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;// 这里真正处理回滚processRollback(defStatus, false);
}
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {try {// 传入时写死的为falseboolean unexpectedRollback = unexpected;try {// 完成前触发操作-删除本地线程的ThreadLoacl的资源信息resourcestriggerBeforeCompletion(status);// 存在保存点,根据我们之前的分析,说明这是一个嵌套调用的事务// 并且内部事务的传播级别为nestedif (status.hasSavepoint()) {// 这里会回滚到定义的保存点status.rollbackToHeldSavepoint();}// 根据我们之前的分析有两种情况会满足下面这个判断// 1.直接调用,传播级别为nested、required、requires_new// 2.嵌套调用,并且内部事务的传播级别为requires_newelse if (status.isNewTransaction()) {// 直接获取当前线程上绑定的数据库连接并调用其rollback方法doRollback(status);}else {// 到这里说明存在事务,但不是一个新事务并且没有保存点// 也就是嵌套调用并且内部事务的传播级别为supports、required、mandatoryif (status.hasTransaction()) {// status.isLocalRollbackOnly,代表事务的结果只能为回滚// 默认是false的,在整个流程中没有看到修改这个属性// isGlobalRollbackOnParticipationFailure// 这个属性的含义是在加入的事务失败时是否回滚整个事务,默认为trueif (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {// 从这里可以看出,但内部的事务发生异常时会将整个大事务标记成回滚doSetRollbackOnly(status);}else {// 进入这个判断说明修改了全局配置isGlobalRollbackOnParticipationFailure// 内部事务异常并不影响外部事务}}else {// 不存在事务,回滚不做任何操作logger.debug("Should roll back transaction but cannot - no transaction available");}// isFailEarlyOnGlobalRollbackOnly这个参数默认为false// unexpectedRollback的值一开始就被赋值成了falseif (!isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = false;}}}catch (RuntimeException | Error ex) {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);throw ex;}// 完成后触发操作-删除本地线程ThreadLocal的同步信息synchronizationstriggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);// unexpectedRollback是false// 这个值如果为true,说明if (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");}}finally {// 在事务完成后需要执行一些清理动作cleanupAfterCompletion(status);}
}

1、触发事务同步的beforeCompletion回调方法

  • 移除本地线程的ThreadLoacl的资源信息resources的SqlSession
/*** AbstractPlatformTransactionManager的方法*/
protected final void triggerBeforeCompletion(DefaultTransactionStatus status) {//如果存在事务同步if (status.isNewSynchronization()) {if (status.isDebug()) {logger.trace("Triggering beforeCompletion synchronization");}//在所有当前已注册的TransactionSynchronization上触发beforeCompletion回调TransactionSynchronizationUtils.triggerBeforeCompletion();}
}/*** TransactionSynchronizationUtils的方法* 在所有当前已注册的TransactionSynchronization上触发beforeCompletion回调。*/
public static void triggerBeforeCompletion() {//遍历synchronizations中绑定到当前线程的TransactionSynchronization集合for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {try {//依次调用beforeCompletion回调方法synchronization.beforeCompletion();} catch (Throwable tsex) {logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex);}}
}// SqlSessionUtils的方法
@Override
public void beforeCompletion() {if (!this.holder.isOpen()) {TransactionSynchronizationManager.unbindResource(sessionFactory);this.holderActive = false;this.holder.getSqlSession().close();}
}

在这里插入图片描述

2、rollbackToHeldSavepoint 回滚保存点

  • 一般情况下,仅内层的nested事务方法会开启保存点
  • 如果具有保存点,那么回滚到为事务保留的保存点,然后立即释放该保存点,并置为null
/*** AbstractTransactionStatus的方法* <p>* 回滚到为事务保留的保存点,然后立即释放该保存点。* <p>* 我们在invokeWithinTransaction方法中知道,completeTransactionAfterThrowing* 方法执行完毕仍然会抛出异常到外层事务中,因此仍然可能导致外层事务的回滚*/
public void rollbackToHeldSavepoint() throws TransactionException {//获取保存点Object savepoint = getSavepoint();if (savepoint == null) {throw new TransactionUsageException("Cannot roll back to savepoint - no savepoint associated with current transaction");}//使用保存点管理器来执行回滚保存点的操作,实际上SavepointManager就是当前的内部事务对象//比如DataSourceTransactionManager的DataSourceTransactionObject//DataSourceTransactionObject继承了JdbcTransactionObjectSupport//回滚操作getSavepointManager().rollbackToSavepoint(savepoint);//释放保存点getSavepointManager().releaseSavepoint(savepoint);//设置保存点为nullsetSavepoint(null);
}/*** JdbcTransactionObjectSupport的方法* <p>* 回滚到给定的JDBC 3.0保存点。*/
@Override
public void rollbackToSavepoint(Object savepoint) throws TransactionException {//获取连接持有者ConnectionHolder conHolder = getConnectionHolderForSavepoint();try {//获取连接Connection并调用rollback方法参数为当前的保存点conHolder.getConnection().rollback((Savepoint) savepoint);//重置当前连接的rollbackOnly为falseconHolder.resetRollbackOnly();} catch (Throwable ex) {//回滚失败抛出异常throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);}
}/*** JdbcTransactionObjectSupport的方法* <p>* 释放给定的JDBC 3.0保存点。*/
@Override
public void releaseSavepoint(Object savepoint) throws TransactionException {//获取连接持有者ConnectionHolder conHolder = getConnectionHolderForSavepoint();try {//获取连接Connection并调用releaseSavepoint方法参数为当前的保存点conHolder.getConnection().releaseSavepoint((Savepoint) savepoint);} catch (Throwable ex) {logger.debug("Could not explicitly release JDBC savepoint", ex);}
}

3、doRollback 回滚事务

  • 直接调用,传播级别为nested、required、requires_new
  • 嵌套调用,并且内部事务的传播级别为requires_new
  • 该方法由具体的事务管理器子类来实现
  • DataSourceTransactionManager的实现很简单,就是调用内部的连接的Connection#rollback方法执行回滚操作
/*** DataSourceTransactionManager的方法** @param status 事务状态,代表当前事务*/
@Override
protected void doRollback(DefaultTransactionStatus status) {//获取内部事务对象DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();//获取内部的连接Connection con = txObject.getConnectionHolder().getConnection();if (status.isDebug()) {logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");}try {//很简单,调用Connection的rollback方法执行回滚con.rollback();} catch (SQLException ex) {throw new TransactionSystemException("Could not roll back JDBC transaction", ex);}
}

4、doSetRollbackOnly 设置仅回滚

  • 外层有事务,内层事务的传播级别为supports、required、mandatory
  • 表示该事务方法和外层的事务方法属于同一个事务
  • 这里的设置的不再是底层的Connection的属性,而是设置的ConnectionHolder的rollbackOnly属性为true
  • 只做回滚标识,等到提交的时候统一不提交
/*** DataSourceTransactionManager的方法* 设置给定的事务仅回滚。仅当当前事务参与现有事务时才回滚调用。*/
@Override
protected void doSetRollbackOnly(DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();if (status.isDebug()) {logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() +"] rollback-only");}//设置为仅回滚,就是设置ResourceHolderSupport的rollbackOnly属性为true,外层事务也将必定回滚txObject.setRollbackOnly();
}/*** 设置ConnectionHolder的rollbackOnly属性为true*/
public void setRollbackOnly() {getConnectionHolder().setRollbackOnly();
}

5、触发事务同步的afterCompletion回调方法

  • 清空当前线程绑定ThreadLoacl的TransactionSynchronization事务同步对象集合
/*** AbstractPlatformTransactionManager的方法* <p>* 触发afterCompletion回调。** @param status           事务状态,代表着当前事务* @param completionStatus TransactionSynchronization常量表示的事务完成状态*                         如果回滚处理成功则是STATUS_ROLLED_BACK,回滚过程中抛出异常则是STATUS_UNKNOWN*/
private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {//如果是新事务同步if (status.isNewSynchronization()) {//获取目前注册的TransactionSynchronization集合List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();//清空当前线程绑定的TransactionSynchronization集合TransactionSynchronizationManager.clearSynchronization();//如果没有事务或者是新事物if (!status.hasTransaction() || status.isNewTransaction()) {if (status.isDebug()) {logger.trace("Triggering afterCompletion synchronization");}//立即调用afterCompletion回调invokeAfterCompletion(synchronizations, completionStatus);} else if (!synchronizations.isEmpty()) {//否则表示有事务但不是新事务//我们参与的现有事务在此Spring事务管理器的范围之外进行了控制,//那么尝试向现有(JTA)事务注册一个afterCompletion回调。registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);}}
}/*** AbstractPlatformTransactionManager的方法** @param synchronizations 当前事务同步* @param completionStatus 事务执行结果*/
protected final void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) {TransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus);
}public static void invokeAfterCompletion(@Nullable List<TransactionSynchronization> synchronizations,int completionStatus) {if (synchronizations != null) {//遍历for (TransactionSynchronization synchronization : synchronizations) {try {//依次调用afterCompletion方法synchronization.afterCompletion(completionStatus);} catch (Throwable tsex) {logger.error("TransactionSynchronization.afterCompletion threw exception", tsex);}}}
}

6、cleanupAfterCompletion 完成后清理

  • 设置当前事务状态为已完成,即completed属性为true
  • 如果是新同步,那么这里清除绑定到当前线程的事务信息,比如事务同步、事务名、事务只读状态、事务隔离级别、事务激活状态
  • 如果是新事务获取的新连接,解绑当前线程连接,重置连接属性
  • 如果存在此前已经被挂起的事务资源suspendedResources,那么这里需要恢复此前的资源,重新绑定之前挂起的数据库资源,重新唤醒并注册此前的同步器,重新绑定各种事务信息
// AbstractPlatformTransactionManager的方法
private void cleanupAfterCompletion(DefaultTransactionStatus status) {// 将事务状态修改为已完成status.setCompleted();// 是否是新的同步// 清理掉线程绑定的所有同步信息// 直接调用时,在任意传播级别下这个条件都是满足的// 嵌套调用时,只有传播级别为not_supported、requires_new才会满足if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}// 是否是一个新的事务// 直接调用下,required、requires_new、nested都是新开的一个事务// 嵌套调用下,只有requires_new会新起一个事务if (status.isNewTransaction()) {// 真正执行清理doCleanupAfterCompletion(status.getTransaction());}// 如果存在挂起的资源,将挂起的资源恢复// 恢复的操作跟挂起的操作正好相反// 就是将之前从线程解绑的资源(数据库连接等)已经同步回调重新绑定到线程上if (status.getSuspendedResources() != null) {if (status.isDebug()) {logger.debug("Resuming suspended transaction after completion of inner transaction");}Object transaction = (status.hasTransaction() ? status.getTransaction() : null);resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());}
}// TransactionSynchronizationManager的方法
public static void clear() {//清除事务同步synchronizations.remove();//清除事务名currentTransactionName.remove();//清除事务只读状态currentTransactionReadOnly.remove();//清除事务隔离级别currentTransactionIsolationLevel.remove();//清除事务有效状态actualTransactionActive.remove();
}// DataSourceTransactionManager实现的方法
protected void doCleanupAfterCompletion(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// 先判断是否是一个新连接// 直接调用,如果真正开启了一个事务必定是个连接// 但是嵌套调用时,只有requires_new会新起一个连接,其余的都是复用外部事务的连接// 这种情况下不能将连接从线程上下文中清除,因为外部事务还需要使用if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.unbindResource(obtainDataSource());}// 恢复连接的状态// 1.重新将连接设置为自动提交// 2.恢复隔离级别// 3.将read only重新设置为falseConnection con = txObject.getConnectionHolder().getConnection();try {if (txObject.isMustRestoreAutoCommit()) {con.setAutoCommit(true);}DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());}catch (Throwable ex) {logger.debug("Could not reset JDBC Connection after transaction", ex);}// 最后,因为事务已经完成了所以归还连接(关闭连接)if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, this.dataSource);}// 清除当前连接持有者的属性,比如transactionActive重置为false,rollbackOnly置为false等等txObject.getConnectionHolder().clear();
}

6.1、resume 恢复事务

  • 重新绑定之前挂起的数据库资源,重新唤醒并注册此前的同步器,重新绑定各种事务信息
  • 我们所说的“挂起”事务,实际上就是将绑定到当前线程的连接对象替换为一个新的连接对象,并且被替换的连接对象保存起来
  • 那么“恢复”的概念就好理解了,由于此前保存了被“挂起”的资源suspendedResources,那么在当前事务或者方法完成之后,我们直接将保存的连接资源再次绑定为当前线程正在使用资源即可,也就是绑定到resources线程本地变量
/*** AbstractPlatformTransactionManager的方法* <p>* 恢复给定的事务。首先委托doResume模板方法,然后恢复事务同步。** @param transaction     当前事务对象* @param resourcesHolder 此前被挂起的资源*/
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)throws TransactionException {if (resourcesHolder != null) {//被挂起的事务资源Object suspendedResources = resourcesHolder.suspendedResources;if (suspendedResources != null) {//首先调用doResume恢复当前事务的资源。事务同步将在此后恢复。//该方法作为模版方法,由子类来实现doResume(transaction, suspendedResources);}//被挂起的事物同步List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;//如果存在此前被挂起的事务同步if (suspendedSynchronizations != null) {//那么这里恢复绑定此前的各种事务属性TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);///唤醒事务同步doResumeSynchronization(suspendedSynchronizations);}}
}/*** DataSourceTransactionManager的方法** @param transaction        当前事务,DataSourceTransactionManager的实现中,该属性没有用到* @param suspendedResources 被挂起的资源*/
@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {//重新将此前挂起的事务以当前数据源为key绑定到当前线程的事务,bindResource方法我们此前就见过了//这就表示"激活"了这个挂起的事务,是不是很简单?TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}// AbstractPlatformTransactionManager的方法
private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) {TransactionSynchronizationManager.initSynchronization();for (TransactionSynchronization synchronization : suspendedSynchronizations) {// SqlSessionUtils的方法// 解绑当前线程ThreadLoacl的resources的SqlSessionsynchronization.resume();// 添加事务同步对象TransactionSynchronizationManager.registerSynchronization(synchronization);}
}

五、清理事务信息 cleanupTransactionInfo

  • 清除当前绑定的事务信息、恢复老的事务信息绑定
/*** TransactionAspectSupport* 重置TransactionInfo的ThreadLocal* 无论是方法正常还是异常完成,都会调用该方法*/
protected void cleanupTransactionInfo(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {if (txInfo != null) {//使用堆栈还原旧的事务TransactionInfo。如果未设置,则为null。txInfo.restoreThreadLocalStatus();}
}/*** TransactionAspectSupport内部类TransactionInfo的方法*/
private void restoreThreadLocalStatus() {//使用堆栈还原旧的事务TransactionInfo。//每一个当前的TransactionInfo都通过oldTransactionInfo属性保留了对前一个TransactionInfo的引用transactionInfoHolder.set(this.oldTransactionInfo);
}

六、提交事务 commitTransactionAfterReturning

  • 目标方法成功执行之后,返回结果之前调用该方法提交事务
// TransactionAspectSupport的方法的方法
protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (logger.isTraceEnabled()) {logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");}//通过事务管理器执行commit提交事务操作//但是如果TransactionStatus.isRollbackOnly()方法被设置为true,那么仍然会回滚txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}
}
  • 核心就是事务管理器的commit方法
/**1. AbstractPlatformTransactionManager的骨干实现2. <p>3. 尝试提交事务,但仍可能会回滚4. 内部委托给isRollbackOnly,doCommit和rollback方法*/
@Override
public final void commit(TransactionStatus status) throws TransactionException {//如果事务已完成,则抛出异常if (status.isCompleted()) {throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");}//获取当前事务状态DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;/** 1 如果事务明确被设置为仅回滚,那么执行回滚*/if (defStatus.isLocalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Transactional code has requested rollback");}//处理回滚,该方法此前就见过了processRollback(defStatus, false);return;}/** shouldCommitOnGlobalRollbackOnly方法用于判断返回是否以全局方式对已标记为仅回滚的事务调用doCommit提交,* 默认实现返回false,即不会提交,而是一起回滚,但是JtaTransactionManager重写返回true* 并且如果当前事务被设置为全局回滚,对于DataSourceTransactionObject来说就是判断内部的ConnectionHolder的rollbackOnly属性** 2 以上条件都返回true,那么表示会执行回滚*/if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");}/** 处理回滚,该方法此前就见过了。注意这里的unexpected参数为true* 对于加入到外层事务的行为,如果在内层方法中进行了回滚,即使异常被捕获,由于被设置为了仅回滚,那么该事物的所有操作仍然会回滚* 并且还会在处理最外层事务方法时抛出UnexpectedRollbackException异常,来提醒开发者所有的外部和内部操作都已被回滚!** 这一般对于内层PROPAGATION_SUPPORTS或者内层PROPAGATION_REQUIRED或者内层PROPAGATION_MANDATORY生效,* 对于内层PROPAGATION_NOT_SUPPORTED则无效。*/processRollback(defStatus, true);return;}/** 3 最后才会真正的提交*/processCommit(defStatus);
}
  • 真正处理提交的逻辑在processCommit方法中
  • 最核心的步骤就是调用了数据库连接对象(Connection)的commit方法
private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;try {boolean unexpectedRollback = false;// 留给子类复写的一个方法,没有做实质性的事情prepareForCommit(status);// 触发同步回调triggerBeforeCommit(status);triggerBeforeCompletion(status);beforeCompletionInvoked = true;// 存在保存点,将事务中的保存点清理掉if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Releasing transaction savepoint");}unexpectedRollback = status.isGlobalRollbackOnly();status.releaseHeldSavepoint();}// 直接调用,传播级别为required、nested、requires_new// 嵌套调用,且传播级别为requires_newelse if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction commit");}// 虽然前面已经检查过rollbackOnly了,在shouldCommitOnGlobalRollbackOnly为true时// 仍然需要提交事务,将unexpectedRollback设置为true,意味着提交事务后仍要抛出异常unexpectedRollback = status.isGlobalRollbackOnly();// 提交的逻辑很简单,调用了connection的commit方法doCommit(status);}else if (isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = status.isGlobalRollbackOnly();}if (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");}}catch (UnexpectedRollbackException ex) {// unexpectedRollback为true时,进入这个catch块// 触发同步回调triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);throw ex;}catch (TransactionException ex) {// connection的doCommit方法抛出SqlException时进入这里if (isRollbackOnCommitFailure()) {// 在doCommit方法失败后是否进行回滚,默认为falsedoRollbackOnCommitException(status, ex);}else {// 触发同步回调triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);}throw ex;}catch (RuntimeException | Error ex) {// 剩余其它异常进入这个catch块if (!beforeCompletionInvoked) {triggerBeforeCompletion(status);}doRollbackOnCommitException(status, ex);throw ex;}// 触发同步回调try {triggerAfterCommit(status);}finally {triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);}}finally {// 跟回滚时一样,做一些清理动作cleanupAfterCompletion(status);}
}

总结

核心类作用

  • TransactionDefinition:通过@Transactional注解指定的事务的属性,包括是否只读、超时、回滚等
  • TransactionStatus:事务的状态,这个状态分为很多方面,比如事务的运行状态(是否完成、是否被标记为rollbakOnly),事务的同步状态(这个事务是否开启了一个新的同步),还有事务是否设置了保存点。更准确的来说,TransactionStatus代表的是被@Transactional注解修饰的方法的状态,只要被@Transactional注解修饰了,在执行通知逻辑时就会生成一个TransactionStatus对象,即使这个方法没有真正的开启一个数据库事务
  • DataSourceTransactionObject:一个数据库事务对象,实际上保存的就是一个数据库连接以及这个连接的状态
  • TransactionInfo:事务相关的所有信息,组合了事务管理器,事务状态,事务定义并持有一个旧的TransactionInfo引用,这个对象在事务管理的流程中其实没有实际的作用,主要的目的是为了让我们在事务的运行过程中获取到事务的相关信息,我们可以直接调用TransactionAspectSupport的currentTransactionInfo方法获取到当前线程绑定的TransactionInfo对象

事务的挂起和恢复

  • 事物的“挂起”和“恢复”,主要是使用到了一系列的ThreadLocal线程本地变量
  • Spring声明式事务对于同一个线程在同一个时刻只能使用(激活)一个资源(比如连接)
    • 当前绑定的资源对象(绑定到resources的线程本地变量中),就是正在使用的资源
    • 并且,通常情况下一个连接只能对应(开启)一个物理事务
    • 因此如果是内层REQUIRES_NEW事务方法,为了开启新的事务,将会获取一个新的连接
  • 我们所说的“挂起”事务,实际上就是将绑定到当前线程的连接对象从线程本地变量中移除
    • 并且被移除的连接对象将会被保存起来(当前的TransactionStatus通过suspendedResources属性保存了此前被挂起的一系列资源,而被挂起的资源中又保存了更久之前被挂起的资源,这样就通过引用保存的方式形成了一个挂起资源链)
    • 对于当前的事务方法,将可能获取并绑定一个新的连接来开启新的事务(比如REQUIRES_NEW),或者不会开启事务(比如NOT_SUPPORTED)
    • 由于此前的连接(事务)在当前内层事务方法执行其间并不会被被进行任何操作执行提交或者回滚,被像“挂起”暂停了一样
  • 明白了事物的“挂起”,那么事务的“恢复”就很简单了
    • 由于我们保存了被“挂起”的资源suspendedResources,那么在当前事务或者方法完成之后,我们直接将保存的连接资源再次绑定为当前线程正在使用资源即可
    • 也就是绑定到resources线程本地变量,后续的事务操作,比如提交和回滚就会又执行我们重新绑定的连接对应的事务了

事务的传播机制

  • 直接调用,要么新建事务,要么以非事务方式运行,也可能抛出异常
传播级别运行方式
requires_new新建事务
nested新建事务
required新建事务
supports非事务的方式运行
not_supported非事务的方式运行
never非事务的方式运行
mandatory抛出异常
  • 嵌套调用,如果外围无事务,那么与直接调用一样,下面只列举外围有事务情况(外部事务的传播级别为requires_new,nested或者required
传播级别运行方式
requires_new新建事务
nested“新建”事务
required直接加入到外部事务
supports直接加入到外部事务
not_supported挂起外部事务,以非事务的方式运行
never抛出异常
mandatory直接加入到外部事务

nested:其实没有新建事务,与外围共用一个事务,只是自己新建了保存点,自己可以回滚提交不影响外围事务,但是外围事务回滚,内层事务也会随之回滚,因为使用的同一个事务


http://www.ppmy.cn/news/1145474.html

相关文章

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.5 键盘事件

本章要实现的整体效果如下&#xff1a; QEvent::KeyPress ​ 键盘按下时&#xff0c;触发该事件&#xff0c;它对应的子类是 QKeyEvent QEvent::KeyRelease ​ 键盘抬起时&#xff0c;触发该事件&#xff0c;它对应的子类是 QKeyEvent 本节通过两个案例来讲解这 2 个事件&…

springboot项目中后台文件上传处理

参考地址:http://www.gxcode.top/code 文件上次核心处理代码: @Autowired private FileUpload fileUpload; //获取资源对象:file-upload-prod.properties@ApiOperation(value = "用户头像修改", notes = "用户头像修改", httpMethod =

Typescript 综合笔记:解读一个github中的React 网页

1 repository来源和效果 zhitern/ntu-scse22-0163-web (github.com) 2 核心代码异同&#xff08;相比于初始创建的代码&#xff09; 2.1 index.html 和初始创建的是一样的 2.2 App.css 和初始创建的是一样的 2.3 index.tsx 唯一”不一样“的是紫色部分,tsx文件中多了一个…

C#导出本机Win32native dll

C# 使用 "3f/DllExport" 工具导出C风格的本机函数 [文 / 张赐荣] 首先&#xff0c;让我们来了解一下什么是争渡读屏软件&#xff0c;以及什么是争渡文本预处理API。争渡读屏软件是一款屏幕朗读软件&#xff0c;用于协助视力障碍人士操作电脑。 争渡文本预处理API是一…

vue,前端打包项目、部署上线

前端项目是在本地的IDE开发的。流程是&#xff1a;开发》打包》上线到生产环境》使用。 vue脚手架只是开发过程中,协助开发的工具,当真正开发完了&#xff0c;脚手架不参与上线。 这时候要用到打包了。 打包后,可以生成,浏览器能够直接运行的网页>就是需要上线的源码! 打…

maven的pom.xml文件显示被删除

文章目录 1.问题情况2.问题分析3.问题解决 1.问题情况 2.问题分析 这些 pom.xml 文件被 maven 视为了忽略文件。 3.问题解决 路径&#xff1a;File --> Settings --> Build&#xff0c;Execution&#xff0c;Deployment --> Build Tools --> Maven --> Ignor…

对比纯软开与嵌入式硬件开发谁更好呢?

对比纯软开与嵌入式硬件开发谁更好呢&#xff1f; 你的纠结和犹豫是理解的&#xff0c;职业选择确实是一个重要的决策。我明白你在嵌入式和软件开发之间犹豫不决的原因。让我给你提供一些建议&#xff0c;帮助你做出更明智的决定。最近很多小伙伴找我&#xff0c;说想要一些嵌入…

【一周安全资讯1007】多项信息安全国家标准10月1日起实施;GitLab发布紧急安全补丁修复高危漏洞

要闻速览 1.以下信息安全国家标准10月1日起实施 2.GitLab发布紧急安全补丁修复高危漏洞 3.主流显卡全中招&#xff01;GPU.zip侧信道攻击可泄漏敏感数据 4.MOVEit漏洞导致美国900所院校学生信息发生大规模泄露 5.法国太空和国防供应商Exail遭黑客攻击&#xff0c;泄露大量敏感…