Spring Transaction 管理原理

摘要

Spring 使用的都是聲明式事務,不過在最開始時 Spring 支持的是編程式事務。本篇講的是 Spring 最第一版本 interface21(如下使用它指代spring的最第一版本代碼) 實現的事務即編程式事務。由於聲明式事務只是提高了易用性,二者的內核是一致的。html

環境與說明

jdk: 1.7
github: https://github.com/SixPenny/spring-framework-0.9.git
IDE: ideajava

在根目下的 docs 目錄中有一個 tutorial.pdf 文件,描述了 Spring 該如何使用,是閱讀代碼的好幫手。git

源碼解析

核心類

interface21 使用的是編程式事務模型,使用上不如聲明式事務模型方便,不過卻有利於咱們看清 Spring 是如何實現事務的。github

Spring 事務的核心類是PlatformTransactionManager, 裏面定義了事務相關的三個方法 獲取事務對象getTransaction,提交事務 commit 和 回滾事務 rollbackweb

TransactionStatus getTransaction(TransactionDefinition definition)
	    throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;

TransactionStatus 類是事務的上下文,後面的 commitrollback 都依賴於它,TransactionDefinition 是事務定義,包含了事務的傳播方式、隔離方式和超時屬性。spring

getTransaction 方法

接口的方法不多,事務開始、事務同步等都是怎麼實現的呢? getTransaction 這個方法的語義包含了這些,能夠在AbstractPlatformTransactionManagergetTransaction模板中找到具體的流程定義。編程

/**
 * This implementation of getTransaction handles propagation behavior and
 * checks non-transactional execution (on CannotCreateTransactionException).
 * Delegates to doGetTransaction, isExistingTransaction, doBegin.
 */
public final TransactionStatus getTransaction(TransactionDefinition definition)
    throws TransactionException {
	try {
		Object transaction = doGetTransaction();
		logger.debug("Using transaction object [" + transaction + "]");
		if (isExistingTransaction(transaction)) {
			logger.debug("Participating in existing transaction");
			return new TransactionStatus(transaction, false);
		}
		if (definition == null) {
			// use defaults
			definition = new DefaultTransactionDefinition();
		}
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new NoTransactionException("Transaction propagation mandatory but no existing transaction context");
		}
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED) {
			// create new transaction
			doBegin(transaction, definition.getIsolationLevel(), definition.getTimeout());
			if (this.transactionSynchronization) {
				TransactionSynchronizationManager.init();
			}
			return new TransactionStatus(transaction, true);
		}
	}
	catch (CannotCreateTransactionException ex) {
		// throw exception if transactional execution required
		if (!this.allowNonTransactionalExecution) {
			logger.error(ex.getMessage());
			throw ex;
		}
		// else non-transactional execution
		logger.warn("Transaction support is not available: falling back to non-transactional execution", ex);
	}
	catch (TransactionException ex) {
		logger.error(ex.getMessage());
		throw ex;
	}
	// empty (-> "no") transaction
	return new TransactionStatus(null, false);
}

咱們來分析一下這個方法spring-mvc

  1. Object transaction = doGetTransaction(); 由子類實現的,返回一個事務對象,須要保證同一個事務返回的事務對象是同一個,事務對象中封裝了鏈接、會話等信息,視具體實現而定。DataSourceTransactionManager 返回了一個封裝了Connection的對象,HibernateTransactionManager 返回了一個封裝了Session 的對象。
if (isExistingTransaction(transaction)) {
	logger.debug("Participating in existing transaction");
	return new TransactionStatus(transaction, false);
}

若是已經存在一個事務,則直接返回(此時還未加入PROPAGATION_REQUIRES_NEW 等屬性,加入以後這個返回變成了handleExistingTransaction方法) 3.mvc

if (definition == null) {
	// use defaults
	definition = new DefaultTransactionDefinition();
}

若是沒有事務定義,就使用默認的事務定義 PROPAGATION_REQUIRED 與 ISOLATION_DEFAULT 4.編輯器

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
	throw new NoTransactionException("Transaction propagation mandatory but no existing transaction context");
}

如何事務定義指定使用 PROPAGATION_MANDATORY 方式,則拋出異常(到這是沒有事務的路徑,前面存在事務直接返回了) 5.

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED) {
	// create new transaction
	doBegin(transaction, definition.getIsolationLevel(), definition.getTimeout());
	if (this.transactionSynchronization) {
		TransactionSynchronizationManager.init();
	}
	return new TransactionStatus(transaction, true);
}

若是事務定義爲PROPAGATION_REQUIRED, 則開啓一個新事務, 模板方法doBegin由子類決定如何開啓一個新事務。
DataSourceTransactionManager 中的事務開啓定義以下

protected void doBegin(Object transaction, int isolationLevel, int timeout) {
	if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
		throw new InvalidTimeoutException("DataSourceTransactionManager does not support timeouts");
	}
	DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
	Connection con = txObject.getConnectionHolder().getConnection();
	logger.debug("Switching JDBC connection [" + con + "] to manual commit");
	try {
		if (isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) {
			logger.debug("Changing isolation level to " + isolationLevel);
			txObject.setPreviousIsolationLevel(new Integer(con.getTransactionIsolation()));
			con.setTransactionIsolation(isolationLevel);
		}
		con.setAutoCommit(false);
	}
	catch (SQLException ex) {
		throw new CannotCreateTransactionException("Cannot configure connection", ex);
	}
	DataSourceUtils.getThreadObjectManager().bindThreadObject(this.dataSource, txObject.getConnectionHolder());
}

能夠看到這裏事務的開啓就是保存當前jdbc connection 的autoCommit現場(在commitrollback中恢復),並將 autoCommit 設爲 false

上面的5步就是事務的開始過程,相比如今功能不全,可是流程更加清晰,理解更加方便。

commitrollback 方法

commit 方法與rollback 方法提供了編程式回滾的功能,對嵌套事務提供支持,還提供了回調功能。

public final void commit(TransactionStatus status) throws TransactionException {
	if (status.isRollbackOnly()) {
		logger.debug("Transactional code has requested rollback");
		rollback(status);
	}
	else if (status.isNewTransaction()) {
		try {
			doCommit(status);
			triggerAfterCompletion(TransactionSynchronization.STATUS_COMMITTED);
		}
		catch (UnexpectedRollbackException ex) {
			triggerAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
			logger.error(ex.getMessage());
			throw ex;

		}
		catch (TransactionException ex) {
			triggerAfterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
			logger.error(ex.getMessage());
			throw ex;
		}
		finally {
			TransactionSynchronizationManager.clear();
		}
	}
}
public final void rollback(TransactionStatus status) throws TransactionException {
	if (status.isNewTransaction()) {
		try {
			doRollback(status);
			triggerAfterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
		}
		catch (TransactionException ex) {
			triggerAfterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
			logger.error(ex.getMessage());
			throw ex;
		}
		finally {
			TransactionSynchronizationManager.clear();
		}
	}
	else if (status.getTransaction() != null) {
		try {
			doSetRollbackOnly(status);
		}
		catch (TransactionException ex) {
			logger.error(ex.getMessage());
			throw ex;
		}
	}
	else {
		// no transaction support available
		logger.info("Should roll back transaction but cannot - no transaction support available");
	}
}

前面說的恢復 autoCommit 屬性是在子類中實現的(現場保存也是在子類中),你們能夠去看DataSourceTransactionManager 類的closeConnection方法。

使用方式

使用方式很是簡單,下面是一個參考資料1 中的例子,很簡單,就很少說了。

public void persistOrderItems() {
      TransactionStatus ts =
              transactionManager.getTransaction(new DefaultTransactionDefinition());
      try {
          long id = dao.save(new OrderItem("BWell Ethernet Cable", 5));
          id = dao.save(new OrderItem("EDrive SSD", 2000));
          transactionManager.commit(ts);
      } catch (Exception e) {
              transactionManager.rollback(ts);
      }
  }

也可使用周邊類中的TransactionTemplate,裏面定義了主流程,咱們只須要定義本身的執行方法就能夠了。

周邊類

TransactionTemplate

Spring 封裝了一個簡單類供咱們使用 TransactionTemplate, 它的核心方法是 execute,雖然叫 Template, 其實是一個回調模式的應用。

public Object execute(TransactionCallback action) throws TransactionException, RuntimeException {
	TransactionStatus status = this.transactionManager.getTransaction(this);
	try {
		Object result = action.doInTransaction(status);
		this.transactionManager.commit(status);
		return result;
	}
	catch (TransactionException tse) {
		throw tse;
	}
	catch (RuntimeException ex) {
		// transactional code threw exception
		this.transactionManager.rollback(status);
		throw ex;
	}
}

TransactionCallback 是咱們定義的須要執行的方法回調,TransactionTemplate 幫咱們處理了提交與回滾操做。(TransactionCallback 在interface21 中只有一個默認實現,尚未返回結果,不過咱們注重的是這個結構)

TransactionSynchronization

TransactionSynchronization 提供了事務同步相關的方法,interface21 中只包含了 void afterCompletion(int status); 方法,如今的Spring 包含了更多的方法,用戶可使用這個註冊事務提交,完成以後的回調任務。

TransactionInterceptor

這是一個cglib 代理實現的事務攔截器,是聲明式事務的雛形,有興趣的能夠看一下

參考網址

  1. Spring - Programmatic Transaction
  2. Spring MVC type conversion : PropertyEditor or Converter?
  3. Spring Property Editor – CustomEditorConfigurer Example
  4. Spring Framework’s WebDataBinder
  5. <Spring>屬性編輯器, PropertyEditor, JavaBean-
相關文章
相關標籤/搜索