天天看点

spring事务原理解析spring事务原理解析

spring事务原理解析

1. 事务

事务是指要执行的一组操作,所要做的事情,可能是一组SQL语句,或者是一些列代码。

事务最典型的例子就是银行转账的问题,客户A给客户B转账100元,正常情况下,应该A账户扣去100元,B账户增加100元。但是因为某些原因,比如网络问题,导致A的余额减去后,B的余额没有成功增加100,如果没有事务保证,就会出现上述的问题,这是不合理的。所以需要事务来保证一系列操作的合理性。

其中事务有四个重要的概念,分别是原子性、一致性、隔离性和持久性。下面分别介绍四种特性:

  1. 原子性:原子性是指一组操作,要么全部执行,要不全部不执行。例如上述的转账例子,A账户扣钱和B账户加钱这两个操作,要么一起执行,要么都不执行。
  2. 一致性:是指事务完成后,无论失败或者成功,数据需要保持一致状态,不能部分数据被修改,而部分修改失败。如上述例子中的转账,不能A账户的数据修改成功,B账户的数据修改失败。
  3. 隔离性:隔离性是指多个用户并发访问数据的时候,每个用户开启一个事务,事务之间相互不影响。例如同时有两个账户给A转钱,不会因为两个操作同时进行而影响到彼此。
  4. 持久性:是指一旦事务结束,数据提交后,数据便被持久化到磁盘,即使发生故障也不会导致数据丢失。例如A给B转账成功后,即使银行的机器发生故障,也不影响A和B的账户余额。

2. spring的事务

2.1 相关接口和类说明

spring事务原理解析spring事务原理解析

2.1.1 PlatformTransactionManager

其中spring最顶层的抽象接口是PlatformTransactionManager,该接口定义类各个平台事务管理器需要实现的方法,其中主要方法如下:

spring事务原理解析spring事务原理解析
  1. TransactionStatus getTransaction(TransactionDefinition definition):该方法根据定义的事务传播行为,返回当前已激活的事务或者创建一个新的事务。如果是新创建的事务,其隔离级别和超时时间的设置在参数TransactionDefinition中定义,如果是加入已有的事务,则这些属性设置被忽略。其中TransactionDefinition定义了传播行为,隔离级别和超时时间等设置,如果传入null则使用默认设置,在类DefaultTransactionDefinition中定义。      
  2. void commit(TransactionStatus status):该方法根据事务的状态,将给定的事务提交、回滚或者不做任何操作。如果该事务被标记为rollback-only,将事务的修改进行回滚。如果该事务不是新建的事务,作为一个已有事务的部分事务,则不提交该部分事务。如果先前的事务被挂起,并新建了一个事务,则提交本事务后,继续执行原来的事务。      
  3. void rollback(TransactionStatus status):该方法将给定的事务回滚。如果该事务不是新建的事务,则将其所参与的事务设置为rollback-only。如果之前的事务被挂起重新创建了一个事务,则将本事务回滚并继续执行原来的事务。      

2.1.2 TransactionDefinition

TransactionDefinition是一个接口,其中包含一些常量定义了事务的传播行为和隔离级别,以及一些抽象方法。常量和方法如下:

spring事务原理解析spring事务原理解析

各个事务传播行为的意义如下:

  1. PROPAGATION_REQUIRED:如果当前存在事务,则加入当前事务,否则新建一个事务;      
  2. PROPAGATION_SUPPORTS:如果当前存在事务,则以事务的方式运行,否则以非事务的方式运行;      
  3. PROPAGATION_MANDATORY:支持当前事务,如果当前不存在事务,则抛出异常;      
  4. PROPAGATION_REQUIRES_NEW:如果当前存在事务,则将当前事务挂起;否则直接新建一个事务。被挂起的事务在新建事务被提交后继续执行。实际上,这个事务隔离级别并不是在所有transaction manager中可用的,特别是对于JtaTransactionManager,JtaTransactionManager需要javax.transaction.TransactionManager的支持才能实现将事务挂起。(英语渣渣,翻译可能有问题,望各位大佬指教,以下是原文)      
  5. PROPAGATION_NOT_SUPPORTED:不支持事务,而总是在非事务的环境下运行,如果存在事务,不会抛出异常;      
  6. PROPAGATION_NEVER:不支持事务,总是在非事务的环境下运行,如果当前存在事务,抛出异常;      
  7. PROPAGATION_NESTED:如果当前存在事务,则作为当前事务的嵌套事务运行。如果当前不存在事务,表现和PROPAGATION_REQUIRED一样,即不存在事务则新建事务。      

各个事务的隔离级别的意义如下:

  1. ISOLATION_DEFAULT:使用下层数据源的默认的事务隔离级别,对于mysql来说是repeatable-read;      
  2. ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED:使用数据库的read-uncommit级别,不能防止脏读、幻读和不可重复读;      
  3. ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED:使用数据库的read-commit隔离级别,不能防止幻读和不可重复读,可以防止脏读;      
  4. ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ:使用数据库的repeatable-read隔离级别,不能防止幻读,可以防止脏读和不可重复读;      
  5. ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE:使用数据库的串行化读隔离级别,能防止脏读、幻读和不可重复读。      

其中接口方法的意义如下:

  1. int getPropagationBehavior():返回该事务的传播行为,返回的是上述PROPAGATION_XXX的常量值;      
  2. int getIsolationLevel():返回该事务的隔离级别,返回的是上述的ISOLATION_XXX常量值;      
  3. int getTimeout():返回timeout设置的值;      
  4. boolean isReadOnly():返回该事务是否为只读事务;      

2.1.3 TransactionStatus

TransactionStatus是一个接口,其继承了SavepointManager,其中定义了接口方法用于新建、回滚和释放保存点,以及判断当期那事务的状态,是否为新建事务,是否已完成等等。接口定义的方法如下:      
spring事务原理解析spring事务原理解析

此处介绍保存点savepoint的意义:在一个事务中,可以定义一个保存点(savepoint),在保存点后作出的一些修改,在发生异常时,可以回滚到保存点,回滚到保存点只会将保存点后的修改回滚,而不影响保存点之前的修改,整个事务的提交会把保存点修改一起提交,整个事务的回滚,也会把保存点回滚。保存点只有在一个激活的事务中才起作用,保存点可以处理一些较为复杂的需求,在嵌套事务中使用。

每个接口方法的意义如下:

  1. Object createSavepoint():创建一个保存点,该方法返回新建保存点的引用。可以通过方法rollbackToSavepoint()将修改回滚到某个保存点。可以通过方法releaseSavepoint释放一个保存点,当一个事务完成后,会自动释放其中的保存点。      
  2. void rollbackToSavepoint(Object savepoint):回滚事务到某个保存点,回滚之后该保存点将被释放。如果当前事务不支持保存点,将抛出NestedTransactionNotSupportedException异常。      
  3. void releaseSavepoint(Object savepoint):显示的释放一个保存点。当事务结束时,会自动释放其中的保存点。      
  4. boolean isNewTransaction():判断当前事务是否是一个新事务,如果是新的事务,返回true,如果是加入到一个存在的事务中,或者没有激活该事务,则返回false。      
  5. boolean hasSavepoint():判断当前事务是否存在保存点。      
  6. void setRollbackOnly():将当前事务设置为rollback-only,这将强制命令事务管理器只能以回滚的方式结束事务,或抛出一个异常。      
  7. boolean isRollbackOnly():判断当前事务是否被设置为rollback-only。      
  8. void flush():将session中的变化刷新回数据库中。      
  9. boolean isCompleted():判断事务是否已经结束,包括回滚和提交。      

2.2 spring事务的原理

spring可以通过配置文件和注解使用事务,而事务的工作主要通过上述的三个类,即PlatformTransactionManager、TransactionDefinition和TransactionStatus共同完成的。如下图:

spring事务原理解析spring事务原理解析

在TransactionDefinition中定义事务传播行为、隔离级别、超时时间和只读状态等,将定义好的TransactionDefinition通过getTransaction方法传入到PlatformTransactionManager中,依据不同平台的实现,返回一个TransactionStatus对象,该对象中包含一个transaction对象,该对象就是产生的事务。

当需要提交或者回滚事务时,是将上述操作获得的TransactionStatus方法传入到commit或者rollback方法中,每个平台的事务管理器也会依据自身的实现和TransactionStatus的状态,对事务进行提交或者回滚。

2.3 核心类源码分析

2.3.1 AbstractPlatformTransactionManager

AbstractPlatformTransactionManager是实现PlatformTransactionManager接口的一个抽象类,该抽象类使用了模板方法模式,实现了PlatformTransactionManager的核心方,如commit、rollback和getTransaction等方法,在实现这些方法时,调用了自身定义的抽象方法,如doCommit,doGetTransaction抽象方法,这些抽象方法需要在具体的实现类中实现,如JtaTransactionManager和HibernateTransactionManager中有自己不同的实现。通过模板方法模式,AbstractPlatformTransactionManager定义了spring处理事务的流程,给具体的实现类提供了模板的方法,而不同的平台根据自身事务的特性进行不同的实现。

该抽象类提供了以下的事务处理流程:

  1. 判断当前是否存在事务;
  2. 根据TransactionDefinition的定义,采用合理的传播行为;
  3. 必要时挂起或者恢复事务;
  4. 提交时检查rollback-only标志;
  5. 回滚是进行合适的回滚修改操作(进行回滚或者设置rollback-only标志);
  6. 如果事务同步已经激活,则触发已注册的同步回调。

事务同步是一个机制用来注册回调方法,以便在事务完成时调用。这主要由内部的数据访问类提供支持,如JDBC、Hibernate和JPA等,使用JTA事务时:事务开始后,会对开始后打开的资源进行注册,并且在事务结束时关闭这些资源,例如这允许重复使用同一个Hibernate session回话在一个事务内。

AbstractPlatformTransactionManager中的一些属性和默认值:

private int transactionSynchronization = SYNCHRONIZATION_ALWAYS; //默认打开事务同步

private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;  //默认超时时间为-1

private boolean nestedTransactionAllowed = false;  //默认不支持嵌套事务

private boolean validateExistingTransaction = false; //当加入一个事务时是否对已存在的事务进行验证,默认为false

private boolean globalRollbackOnParticipationFailure = true; //设置当加入一个全局事务失败时,是否将这个全局事务设置为rollback-only,默认为true,例如在PROPAGATION_REQUIRES传播属性中,加入一个事务失败后,被加入的事务会被设置为rollback-only

private boolean failEarlyOnGlobalRollbackOnly = false; //设置当一个全局事务被标记为rollback-only时,是否让该全局事务尽早失败。默认为false

private boolean rollbackOnCommitFailure = false; //设置是否在doCommit调用失败后调用doRollback方法
           

getTransaction(TransactionDefinition definition)方法:

该方法根据TransactionDefinition返回一个TransactionStatus对象。其中调用了doGetTransaction、isExistingTransaction、doBegin等模板方法,源码如下(需要深入理解的建议自己看源码,代码太多了,不能面面俱到):

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
		Object transaction = doGetTransaction(); //doGetTransaction为抽象方法,各平台根据自身特性实现

		if (definition == null) {
			// 如果没有传入TransactionDefinition 则使用默认的definition
			definition = new DefaultTransactionDefinition();
		}

		if (isExistingTransaction(transaction)) {
			// 根据传播行为判断如何处理
			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());
		}

		// 当前不存在事务,根据传播行为分别处理
		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);
			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 ex) {
				resume(null, suspendedResources);
				throw ex;
			}
			catch (Error err) {
				resume(null, suspendedResources);
				throw err;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
		}
	}
           

commit方法的实现,对事务进行提交或者回滚,如果设置为rollback-only则回滚,源码如下:

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;
		if (defStatus.isLocalRollbackOnly()) {
			logger.debug("Transactional code has requested rollback");
			processRollback(defStatus);
			return;
		}
		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			processRollback(defStatus);
			// Throw UnexpectedRollbackException only at outermost transaction boundary
			// or if explicitly asked to.
			if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
			return;
		}

		processCommit(defStatus);
	}
           
rollback(TransactionStatus status):      

回滚事务,源码如下:

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);
	}
           

2.4 总结

似乎写的有点戛然而止的感觉,源码很多较复杂,难以一篇文章内全部分析,具体HibernateTransactionManager和JtaTransactionManager的方法实现,在其他文章里再做详细分析吧。有需要的同学可以自己查看源码,文章总有疏略和错误的地方,望大家谅解。

总的来说,spring的事务实现主要通过以下三个类来实现:

spring事务原理解析spring事务原理解析

每个平台实现的详细过程,且听下回分析。(或者自己去看源码吧~~)

继续阅读