Spring事务

1.1 前言

  • spring对事务划分:分为单数据库事务和分布式事务(JTA),而单数据库事务中又根据具体API的不同,区分了datasource、hibernate、jpa、jdo下的事务管理实现。
  • Spring事务传播行为:分为嵌套和独立事务,通过不同的传播行为配置,不同的业务方法可通过事务AOP的配置,实现开启新事务、作为嵌套事务或者加入当前事务等行为。

1.2 spring事务管理器

1.2.1 对象模型

spring中对事务的主要的抽象分为六个部分。

  • ***TransactionDefinition:***事务配置对象。包含事务的隔离级别、传播行为、超时等配置。
  • ***TransactionObject:***事务对象。包含connection连接对象等属性。
  • ***TransactionStatus:***事务上下文。包含当前事务对象、被挂起的事务等属性。
  • ***transactionManager:***事务管理器。主要事务的操作,事务的开启、挂起、恢复、提交和回滚。
  • ***TransactionInterceptor:***事务拦截器。用于事务的AOP实现。
  • TransactionSynchronizationManager: 事务资源同步管理。主要包括事务只读、隔离,connection的属性。
1.2.1.1 TransactionDefinition

这里写图片描述

Spring调用方法时会读取事务相关属性

1.2.1.2 TransactionObject

这里写图片描述

此对象代表着从"jdbc事务API"这一段中可知,jdbc类型的不同事务一定包含有不同的connection.此外,由于事务传播行为的不同,每一个事务还需要需要两个状态分别标识这个事务是否需要回滚以及当前事务是否是活跃状态。

spring中DataSourceTransactionObject是datasource方式事务对象的实现,并通过父类中的ConnectionHolder来持有Connection对象以及标识事务的活跃及回滚状态。

1.2.1.3 TransactionStatus

这里写图片描述
事务上下文,包含了当前事务及当前事务相关配置信息,同时为了满足传播性,当事务挂起时,保存挂起事务相关信息(属性:suspendedResources,事务挂起),当当前事务执行完毕,会恢复挂起的事务。

1.2.1.4 transactionManager

这里写图片描述

事务管理器,事务管理器提供了事务的开启、提交、回滚、挂起、恢复等入口,获取事务时传入TransactionDefinition,返回事务上下文TransactionStatus;提交事务传入TransactionStatus,操作事务上下文;回滚事务,传入TransactionStatus,操作事务上下文。

1.2.1.5 TransactionInterceptor

继承自MethodInterceptor方法,项目启动实例化Bean时,会判断该Bean是否支持事务,如果有则会生成代理类;当调用该Bean方法时,代理类会执行的invoke方法。

1.2.1.6 TransactionSynchronizationManager

这里写图片描述

  • ***resources:***保存了数据源,与ConnectionHolder对象信息
  • ***currentTransactionReadOnly:***只读
  • ***currentTransactionIsolationLevel:***隔离级别
  • ***actualTransactionActive:***事务状态是否有事务

1.2.2 业务方法事务共享原理

  1. 开启事务的时候,先从TransactionSynchronizationManager中获取当前线程的connection对象,如果没有则创建事务,会将事务中connection对象放到TransactionSynchronizationManager。
  2. 操作数据库的时候,获取conection也从TransactionSynchronizationManager中获得连接资源,例如:mybatis与Spring整合,在SpringManagedTransaction.getConnectionDat()方法中利用DataSourceUtils获取连接信息,在DataSourceUtils中获取connection对象,如果开启事务,会从TransactionSynchronizationManager获取,否则从数据源中直接拿事务。
  3. 提交、回滚的时候,从TransactionSynchronizationManager获取connection对象进行数据库操作

1.3 事务处理流程

1.3.1 开启

时序图:

这里写图片描述

流程图:

获取事务:
这里写图片描述
完善事务子流程:
这里写图片描述
把事务记录保存到当前线程中子流程:
这里写图片描述
嵌套事务处理子流程:
这里写图片描述

  1. 传播性为PROPAGATION_NEVER:直接抛出异常
  2. 传播性为PROPAGATION_NOT_SUPPORTED:挂起原有的事务,将事务上下文transaction=null、newTransaction=false、suspendedResources=挂起的事务。
  3. 传播性为PROPAGATION_REQUIRES_NEW:挂起原来的事务,将事务上下文
    transaction=transaction、newTransaction=true、suspendedResources=挂起的事务。
  4. 传播性为PROPAGATION_NESTED:调用步骤5。
  5. 调用isNestedTransactionAllowed()方法,判断事务支持嵌套事务(JTA不支持),真步骤6,否则7。
  6. 将事务上下文
    transaction=transaction、newTransaction=false、suspendedResources=null,并设置savepoint属性。
  7. 将事务上下文
    transaction=transaction、newTransaction=true、suspendedResources=null。

1.3.2 提交

这里写图片描述

1.3.3 回滚

这里写图片描述
如果status有savepoint就回滚到保存的回滚点,这个是NESTED的传播机制处理

if (status.hasSavepoint()) {
	if (status.isDebug()) {
		logger.debug("Rolling back transaction to savepoint");
	}
	status.rollbackToHeldSavepoint();
}

如果这个status是新事务,则滚回。每一个service调用的方法,织入事务,都有一个状态。如果是共享事务的service,除了是事务创建最外层的service,它的isNewTransaction是true,其它所共享事务的service的status状态的isNewTransaction为false

else if (status.isNewTransaction()) {
	if (status.isDebug()) {
		logger.debug("Initiating transaction rollback");
	}
	doRollback(status);
}

打个比方

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void register(RegisterDTO dto) {
        userService.addUser(dto);
}
@Transactional(propagation=Propagation.REQUIRES)
public void addUser(UserDTO dto) {
   
}

register方法创建一个新的事务,isNewTransaction为true。userService传播机制为REQUIRES,加入到这个事务中,它们的status的isNewTransaction为false, 从上面看,只有到了register才真正的回滚

1.4 源码

在项目启动时,Sping利用AOP动态扫描需要代理的类生成代理对象,在方法调用时会走代理对象。TransactionInterceptor 是事务拦截器

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
	...省略
	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}
}
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
	@Nullable
	protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		// If the transaction attribute is null, the method is non-transactional.
		// 获取事务配置属性,在项目启动时会缓存,所以此刻从缓存中取出
		TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			Object retVal = null;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			try {
				Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
					TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
					try {
						return invocation.proceedWithInvocation();
					}
					catch (Throwable ex) {
						if (txAttr.rollbackOn(ex)) {
							// A RuntimeException: will lead to a rollback.
							if (ex instanceof RuntimeException) {
								throw (RuntimeException) ex;
							}
							else {
								throw new ThrowableHolderException(ex);
							}
						}
						else {
							// A normal return value: will lead to a commit.
							throwableHolder.throwable = ex;
							return null;
						}
					}
					finally {
						cleanupTransactionInfo(txInfo);
					}
				});

				// Check result state: It might indicate a Throwable to rethrow.
				if (throwableHolder.throwable != null) {
					throw throwableHolder.throwable;
				}
				return result;
			}
			catch (ThrowableHolderException ex) {
				throw ex.getCause();
			}
			catch (TransactionSystemException ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
					ex2.initApplicationException(throwableHolder.throwable);
				}
				throw ex2;
			}
			catch (Throwable ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
				}
				throw ex2;
			}
		}
	}

	@SuppressWarnings("serial")
	protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// If no name specified, apply method identification as transaction name.
		if (txAttr != null && txAttr.getName() == null) {
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				// 获取事务上下文
				status = tm.getTransaction(txAttr);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
							"] because no transaction manager has been configured");
				}
			}
		}
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}
}
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
@Override
	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
		Object transaction = doGetTransaction();

		// Cache debug flag to avoid repeated checks.
		boolean debugEnabled = logger.isDebugEnabled();

		if (definition == null) {
			// Use defaults if no transaction definition given.
			definition = new DefaultTransactionDefinition();
		}

		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}

		// Check definition settings for new transaction.
		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
			}
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				// 开启事务
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + definition);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
		}
	}
}

Spring与Mybatis整合事务
在这里插入图片描述
注:本篇文章中时序图、流程图、类图下载地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值