1. 程式人生 > >(十一)Spring事務處理

(十一)Spring事務處理

        Spring的宣告式事務處理的即開即用特性為使用者提供了很大的方便,在使用Spring時,我們絕大多數情況下還是使用其宣告式事務處理。宣告式事務處理涉及Spring框架對事務處理的統一管理,以及對併發事務和事務屬性的處理,是一個比較複雜的過程,下面瞭解一下Spring框架宣告式事務處理功能的具體實現。

一、事務的建立

前一篇文章講到對Spring事務攔截器TransactionInterceptor回撥方法invoke的原始碼分析中,我們瞭解到在進行事務處理前,首先根據是否是CallbackPreferringPlatformTransactionManager型別的事務處理器分別通過下面兩個方法建立事務資訊物件:

TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);   
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);   

二、事務的掛起

如果當前執行緒存在事務,但事務傳播特性又要求開啟新事務時,需要將已有的事務進行掛起,事務的掛起涉及執行緒與事務資訊的儲存,實現原始碼如下:

protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
		//如果事務是啟用的,且當前執行緒事務同步機制也是啟用狀態
		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			//掛起當前執行緒中所有同步的事務
			List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
			try {
				Object suspendedResources = null;
				//把掛起事務的操作交由具體的事務處理器處理
				if (transaction != null) {
					suspendedResources = doSuspend(transaction);
				}
			//線上程中儲存與事務處理有關的資訊,並將執行緒裡有關的執行緒區域性變數重置
				String name = TransactionSynchronizationManager.getCurrentTransactionName();
//重置當前執行緒中事務相關的執行緒區域性變數
TransactionSynchronizationManager.setCurrentTransactionName(null);
				boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
	TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
				Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
	TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
				boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
	TransactionSynchronizationManager.setActualTransactionActive(false);
				//將當前執行緒中事務相關資訊儲存
				return new SuspendedResourcesHolder(
						suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
			}
			//對事務掛起操作中產生異常和錯誤的處理
			catch (RuntimeException ex) {
				doResumeSynchronization(suspendedSynchronizations);
				throw ex;
			}
			catch (Error err) {
				doResumeSynchronization(suspendedSynchronizations);
				throw err;
			}
		}
		//如果事務是啟用的,但是事務同步機制不是啟用的,則只需要儲存事務狀態,不
//需要重置事務相關的執行緒區域性變數
		else if (transaction != null) {
			Object suspendedResources = doSuspend(transaction);
			return new SuspendedResourcesHolder(suspendedResources);
		}
		//事務和事務同步機制都不是啟用的,則不要想處理
		else {
			return null;
		}
	}

三、事務的提交

當事務方法處理成功之後,需要將當前事務提交,將更改同步到資料庫中,事務提交的實現原始碼如下:

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()) {
			if (defStatus.isDebug()) {
				logger.debug("Transactional code has requested rollback");
			}
			//處理事務回滾
			processRollback(defStatus);
			return;
		}
		//如果事務沒有被標記為回滾時提交,且事務狀態時全域性回滾
		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}
			//回滾處理
			processRollback(defStatus);
			//如果事務狀態時新事務,或者在全域性回滾時失敗
			if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
			return;
		}
		//處理提交
		processCommit(defStatus);
	}
	//提交處理操作
	private void processCommit(DefaultTransactionStatus status) throws TransactionException {
		try {
			boolean beforeCompletionInvoked = false;
			try {
				//事務提交的準備工作,有具體的事務處理器完成
				prepareForCommit(status);
				triggerBeforeCommit(status);
				triggerBeforeCompletion(status);
				beforeCompletionInvoked = true;
				boolean globalRollbackOnly = false;
				//如果事務狀態是新事務,或者全域性回滾失敗
				if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
					//設定事務全域性回滾
					globalRollbackOnly = status.isGlobalRollbackOnly();
				}
				//巢狀事務處理
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Releasing transaction savepoint");
					}
					//釋放掛起事務儲存點
					status.releaseHeldSavepoint();
				}
				//如果當前事務是新事務
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction commit");
					}
					//呼叫具體事務處理器提交事務
					doCommit(status);
				}
				//如果事務被標記為全域性回滾
				if (globalRollbackOnly) {
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			//提交過程中產生未預期的回滾異常,則回滾處理
			catch (UnexpectedRollbackException ex) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
				throw ex;
			}
			//對提交過程中產生的事務異常處理
			catch (TransactionException ex) {
				//如果回滾失敗,則進行回滾異常處理
				if (isRollbackOnCommitFailure()) {
					doRollbackOnCommitException(status, ex);
				}
				else {
					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				}
				throw ex;
			}
			//對提交過程中產生的異常處理
			catch (RuntimeException ex) {
				//如果不是在完成前呼叫的
				if (!beforeCompletionInvoked) {
					//觸發完成前的回撥方法
					triggerBeforeCompletion(status);
				}
				//進行回滾異常處理
				doRollbackOnCommitException(status, ex);
				throw ex;
			}
//對提交過程中產生的錯誤處理
			catch (Error err) {
				if (!beforeCompletionInvoked) {
					triggerBeforeCompletion(status);
				}
				doRollbackOnCommitException(status, err);
				throw err;
			}
			//觸發提交之後的回撥操作
			try {
				triggerAfterCommit(status);
			}
			finally {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}
		}
		//提交完成之後清除事務相關狀態
		finally {
			cleanupAfterCompletion(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);
	} 
//回滾操作
private void processRollback(DefaultTransactionStatus status) {
		try {
			try {
				//觸發完成前的回撥操作
				triggerBeforeCompletion(status);
				//巢狀事務回滾處理
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Rolling back transaction to savepoint");
					}
					//回滾掛起在儲存點的事務
					status.rollbackToHeldSavepoint();
				}
				//當前事務中建立新事務的回滾操作
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction rollback");
					}
					//回滾處理,由具體的事務處理器實現
					doRollback(status);
				}
				//如果在當前事務中沒有新建事務
				else if (status.hasTransaction()) {
					//如果當前事務狀態為本地回滾,或全域性回滾失敗
					if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
						if (status.isDebug()) {
							logger.debug(
									"Participating transaction failed - marking existing transaction as rollback-only");
						}
						//設定當前事務狀態為回滾
						doSetRollbackOnly(status);
					}
					//當前事務狀態沒有設定為本地回滾,且沒有產生全域性回滾失敗,則
//由執行緒中的前一個事務來處理回滾,這個步驟任何處理
					else {
						if (status.isDebug()) {
							logger.debug(
									"Participating transaction failed - letting transaction originator decide on rollback");
						}
					}
				}
				//如果當前執行緒沒有事務
				else {
					logger.debug("Should roll back transaction but cannot - no transaction available");
				}
			}
			//對回滾操作過程中的執行時異常和錯誤的處理
			catch (RuntimeException ex) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw ex;
			}
			catch (Error err) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw err;
			}
			//回滾操作完成後,觸發回滾之後回撥操作
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
		}
		//清除回滾之後事務狀態資訊
		finally {
			cleanupAfterCompletion(status);
		}
	}