1. 程式人生 > >Spring事務處理的實現:事務處理的實現

Spring事務處理的實現:事務處理的實現

目錄

 

事務處理的程式設計式使用

宣告式事務

事物的建立

事務的掛起

事務的提交

事務的回滾

具體事務處理器的實現


事務處理的程式設計式使用

    書上首先給出了事務處理的程式設計式使用

之後書上為我們解釋了Spring框架對事務處理的統一管理,以及對併發事務和事務屬性的處理。

宣告式事務

事物的建立

書上的程式碼中我們可以從invoke方法中找到事務建立方法

@Override
	public Object invoke(final 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);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
			@Override
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
		});
	}

進入到 invokeWithinTransaction方法中我們可以看到關於事務的建立

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

事務的建立由具體的事務管理器來完成,它建立了一個TransactionStatus物件,這個方法封裝了底層的事務物件的建立。

Object transaction = doGetTransaction();

這個doGetTransaction函式是一個抽象函式,transaction物件的取得由具體的事務處理器實現。

之後是根據事務的屬性對要建立的事務進行初始化,屬性設定完畢後,就是建立事務的呼叫過程,由具體的事務處理器完成。

	doBegin(transaction, definition);

這個方法的實現可以到DataSourceTransactionManager類中進行檢視,之後我會講解這個類。當我們的事務建立完成之後(也就是TransactionStatus建立完成後)。這個TransactionStatus物件實際上最後要交由TransactionInfo 物件來管理持有。關於這個TransactionInfo 物件的準備。我們來看這一行程式碼。

return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,
			TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {

		TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
		if (txAttr != null) {
			// We need a transaction for this method...
			if (logger.isTraceEnabled()) {
				logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
			}
			// The transaction manager will flag an error if an incompatible tx already exists.
			txInfo.newTransactionStatus(status);
		}
		else {
			// The TransactionInfo.hasTransaction() method will return false. We created it only
			// to preserve the integrity of the ThreadLocal stack maintained in this class.
			if (logger.isTraceEnabled())
				logger.trace("Don't need to create transaction for [" + joinpointIdentification +
						"]: This method isn't transactional.");
		}

		// We always bind the TransactionInfo to the thread, even if we didn't create
		// a new transaction here. This guarantees that the TransactionInfo stack
		// will be managed correctly even if no transaction was created by this aspect.
		txInfo.bindToThread();
		return txInfo;
	}

可以看到在這個方法,首先將我們的TransactionStatus物件設定到TransactionInfo物件中,然後為TransactionInfo物件繫結到執行緒。這裡我只是大概進行了講述,書上還包括了對於事務傳播屬性在事務建立時的處理,可以在原書中找到對應的方法和程式碼,我就不再說明。可以參考書中講解。這樣我們的事務建立就完成了。

事務的掛起

NOT_SUPPORTED:宣告方法不需要事務。如果方法沒有關聯到一個事務,容器不會為它開啟事務。如果方法在一個事務中被呼叫,該事務會被掛起,在方法呼叫結束後,原先的事務便會恢復執行。

例如 方法A支援事務 方法B不支援事務。  方法A呼叫方法B。 在方法A開始執行時,系統為它建立Transaction,方法A中對於資料庫的處理操作,會在該Transaction的控制之下。 這時,方法A呼叫方法B,方法A開啟的 Transaction將掛起,方法B中任何資料庫操作,都不在該Transaction的管理之下。 當方法B返回,方法A繼續執行,之前的Transaction回覆,後面的資料庫操作繼續在該Transaction的控制之下 提交或回滾。

這是事務掛起的含義,那麼在Spring中是如何實現事務的掛起的呢。

if (transaction != null) {
					suspendedResources = doSuspend(transaction);
				}

事務的掛起交由具體的事務處理器(例如DataSourceTransactionManager)來完成。之後需要保存於事務處理有關的資訊,並將執行緒裡相關的ThreadLocal(本地執行緒變數副本)變數重置。

事務的提交

事務的提交分為兩種,一種是成功完成對資料庫的操作,另一種是出現異常進行回滾。事務提交的入口是在TransactionInterceptor的invoke方法中實現的

	commitTransactionAfterReturning(txInfo);
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
		if (txInfo != null && txInfo.hasTransaction()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
			}
			txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
		}
	}

進入到commit方法中

執行回滾

processRollback(defStatus);

執行提交

processCommit(defStatus);

在事務的提交過程中,可以看到具體的事務提交doCommit由具體的事務處理器DataSourceTransactionManager來完成。

事務的回滾

事務的回滾處理中包括巢狀事務的回滾處理,其中包括當前事務呼叫方法中新建事務的回滾處理和如果在當前事務呼叫方法中沒有新建事務的回滾處理。這些關於事務的具體處理都是在DataSourceTransactionManager來完成的。

那麼接下來我們就來看看具體事務處理器的實現

具體事務處理器的實現

這裡我們以DataSourceTransactionManager的實現為例為例

@Override
	protected Object doGetTransaction() {
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		ConnectionHolder conHolder =
				(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

這個方法講的是如何進行事務的建立,我們可以看到事務的建立首先要實現對資料庫的繫結連線,它是在第一個事務開始的地方與執行緒繫結的。

接下里的方法是處理事務開始的地方。也是對於事務的屬性一些基本配置。

	@Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
				Connection newCon = this.dataSource.getConnection();
				if (logger.isDebugEnabled()) {
					logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				}
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}

			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();

			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);

			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}

			prepareTransactionalConnection(con, definition);
			txObject.getConnectionHolder().setTransactionActive(true);

			int timeout = determineTimeout(definition);
			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
			}

			// Bind the connection holder to the thread.
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
			}
		}

		catch (Throwable ex) {
			if (txObject.isNewConnectionHolder()) {
				DataSourceUtils.releaseConnection(con, this.dataSource);
				txObject.setConnectionHolder(null, false);
			}
			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}

事務的提交過程,也是通過獲取Connection來進行提交。

@Override
	protected void doCommit(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Committing JDBC transaction on Connection [" + con + "]");
		}
		try {
			con.commit();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not commit JDBC transaction", ex);
		}
	}

事務的回滾,呼叫的是Conncetion的rollback方法,這些都是和我們最開始學習jdbc操作資料是相同的。

@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 {
			con.rollback();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
		}
	}

這個類還有關於事務的掛起和喚醒操作。在這裡我沒有一一列出。

以上全部就是關於事務的原理解讀,Spring的事務機制可以理解為AOP+jdbc對事務的支援。使我們配置切面的同時為其增加事務管理屬性,方便我們對於我們業務的監控。通過這三篇部落格的總結,我發現書上對於事務的解讀還是比較系統化的。但是對於初學來看,其中的一些方法的呼叫還不是十分熟悉。所以和AOP機制相同,下篇文章我打算通過畫一張圖來解讀事務的原理實現。