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
和 回滾事務 rollback
web
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException;
TransactionStatus
類是事務的上下文,後面的 commit
與 rollback
都依賴於它,TransactionDefinition
是事務定義,包含了事務的傳播方式、隔離方式和超時屬性。spring
getTransaction
方法接口的方法不多,事務開始、事務同步等都是怎麼實現的呢? getTransaction
這個方法的語義包含了這些,能夠在AbstractPlatformTransactionManager
的getTransaction
模板中找到具體的流程定義。編程
/** * 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
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
現場(在commit
或 rollback
中恢復),並將 autoCommit
設爲 false
。
上面的5步就是事務的開始過程,相比如今功能不全,可是流程更加清晰,理解更加方便。
commit
與 rollback
方法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 代理實現的事務攔截器,是聲明式事務的雛形,有興趣的能夠看一下