本文是<实现 Spring 的事务控制>系列文章中一篇。本文假设读者已经阅读并理解《实现 Spring 的事务控制,之九(整合七种传播行为)》。其实本文大多数时间是在贴代码。
事务控制的完整代码位于:http://git.oschina.net/zycgit/hasor,Hasor-JDBC子项目。目前存放于develop 分支上。
在开始实现事务管理器之前,我们需要知道。这个系统中都有那些元素?
数据库连接:
数据库连接是封装的最基本的元素,我们上文中提到的引用计数可以封装在这个对象上。
资源管理器:
首先程序不能直接通过 DataSource 取得数据库连接,而是必须通过资源管理器去申请。它有一些充当数据库连接池的角色。但是请大家千万要和数据库连接池区分开,资源管理器在这里的主要作用是管理当前连接。
事务状态:
事务状态是封装了“new”、“suspent”、“savepoint”等状态的封装对象,每当从事务管理器中开启一个行为的事务之后。事务管理器都会创建一个事务状态用以表示。当递交事务的时候也需要程序提供相应的状态,告诉事务管理器执行那个点上的事务操作。
事务管理器:
一切元素的控制中心,它就是核心部件。专门用来实现事务控制的。
---------------------------------------------------------------------------------
构建程序逻辑框架:
public class DefaultTransactionManager implements TransactionManager { /**开启事务*/ public final TransactionStatus getTransaction(TransactionBehavior behavior) throws SQLException { return getTransaction(behavior, TransactionLevel.ISOLATION_DEFAULT); }; /**开启事务*/ public final TransactionStatus getTransaction(TransactionBehavior behavior, TransactionLevel level) throws SQLException { ...... } /**判断连接对象是否处于事务中,该方法会用于评估事务传播属性的处理方式。 */ private boolean isExistingTransaction(TransactionStatus defStatus) throws SQLException { ...... }; /**初始化一个新的连接,并开启事务。*/ protected void doBegin(TransactionStatus defStatus) throws SQLException { ...... } /**递交事务*/ public void commit(TransactionStatus status) throws SQLException { ...... } /**回滚事务*/ public void rollBack(TransactionStatus status) throws SQLException { ...... } // /**挂起事务。*/ protected void suspend(DefaultTransactionStatus defStatus) { ...... } /**恢复被挂起的事务。*/ protected void resume(DefaultTransactionStatus defStatus) { ...... } /**commit,rollback。之后的清理工作,同时也负责恢复事务和操作事务堆栈。*/ private void cleanupAfterCompletion(DefaultTransactionStatus defStatus) { } /**获取数据库连接(线程绑定的)*/ protected TransactionObject doGetConnection(DefaultTransactionStatus defStatus) { ...... }; /**释放数据库连接*/ protected void releaseConnection(DefaultTransactionStatus defStatus) { ...... } }
getTransaction方法
这个方法是我们用于处理开启事务的方法,在上一篇文章中开启事务方面的程序都是从这里开始写的。当创建好事务应当返回一个事务状态对象用 TransactionStatus 接口去封装。
isExistingTransaction方法
这个方法是专门用于判断数据库连接是否存在事务的。我们可以想象,它一定会和 Connection 打交道的。在前面几篇文章中我已经介绍过,几乎每种传播行为都会去判断是否存在事务。因此单独分离出这个方法也比较有用。
doBegin方法
处理开启事务的操作,还记得new状态把。凡是没有开启事务的数据库连接只要开启了事务都是满足了new状态的。因此设置new状态的代码就可以在这里去实现它。
commit方法、rollBack方法
事务的递交和回滚操作入口,在这两个方法里会去判断那一大堆状态以及如何进行递交或回滚的操作。
suspend方法、resume方法
挂起和恢复,挂起方法主要做的事情就是将当前数据库连接清除。然后重新申请一个连接作为当前连接,恢复方法是将一个数据库连接重新作为当前连接。
cleanupAfterCompletion方法
负责清理事务状态的方法。
doGetConnection方法
负责申请数据库连接,并处理引用计数+1。
releaseConnection方法
负责释放连接,引用计数-1。
---------------------------------------------------------------------------------
下面这个是getTransaction方法的实现:
/**开启事务*/ public final TransactionStatus getTransaction(TransactionBehavior behavior, TransactionLevel level) throws SQLException { Hasor.assertIsNotNull(behavior); Hasor.assertIsNotNull(level); //1.获取连接 DefaultTransactionStatus defStatus = new DefaultTransactionStatus(behavior, level); defStatus.setTranConn(doGetConnection(defStatus)); /*------------------------------------------------------------- | 环境已经存在事务 | | PROPAGATION_REQUIRED :加入已有事务(不处理) | RROPAGATION_REQUIRES_NEW :独立事务(挂起当前事务,开启新事务) | PROPAGATION_NESTED :嵌套事务(设置保存点) | PROPAGATION_SUPPORTS :跟随环境(不处理) | PROPAGATION_NOT_SUPPORTED:非事务方式(仅挂起当前事务) | PROPAGATION_NEVER :排除事务(异常) | PROPAGATION_MANDATORY :强制要求事务(不处理) ===============================================================*/ if (this.isExistingTransaction(defStatus) == true) { /*RROPAGATION_REQUIRES_NEW:独立事务*/ if (behavior == RROPAGATION_REQUIRES_NEW) { this.suspend(defStatus);/*挂起当前事务*/ this.doBegin(defStatus);/*开启新事务*/ } /*PROPAGATION_NESTED:嵌套事务*/ if (behavior == PROPAGATION_NESTED) { defStatus.markHeldSavepoint();/*设置保存点*/ } /*PROPAGATION_NOT_SUPPORTED:非事务方式*/ if (behavior == PROPAGATION_NOT_SUPPORTED) { this.suspend(defStatus);/*挂起事务*/ } /*PROPAGATION_NEVER:排除事务*/ if (behavior == PROPAGATION_NEVER) { throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'"); } return pushStack(defStatus);/*入栈*/ } /*------------------------------------------------------------- | 环境不经存在事务 | | PROPAGATION_REQUIRED :加入已有事务(开启新事务) | RROPAGATION_REQUIRES_NEW :独立事务(开启新事务) | PROPAGATION_NESTED :嵌套事务(开启新事务) | PROPAGATION_SUPPORTS :跟随环境(不处理) | PROPAGATION_NOT_SUPPORTED:非事务方式(不处理) | PROPAGATION_NEVER :排除事务(不处理) | PROPAGATION_MANDATORY :强制要求事务(异常) ===============================================================*/ /*PROPAGATION_REQUIRED:加入已有事务*/ if (behavior == PROPAGATION_REQUIRED || /*RROPAGATION_REQUIRES_NEW:独立事务*/ behavior == RROPAGATION_REQUIRES_NEW || /*PROPAGATION_NESTED:嵌套事务*/ behavior == PROPAGATION_NESTED) { this.doBegin(defStatus);/*开启新事务*/ } /*PROPAGATION_MANDATORY:强制要求事务*/ if (behavior == PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"); } // return pushStack(defStatus);/*入栈*/ }
有兴趣的同学可以逐行分析,该方法的实现原理是依照《整合七种传播行为》一文。
下面这段代码展示了如何封装数据库连接,包括了如何处理引用计数:
public class ConnectionHolder implements SavepointManager { private int referenceCount; private DataSource dataSource; private Connection connection; public ConnectionHolder(DataSource dataSource) { this.dataSource = dataSource; } /**增加引用计数,一个因为持有人已被请求。*/ public synchronized void requested() { this.referenceCount++; } /**减少引用计数,一个因为持有人已被释放。*/ public synchronized void released() { this.referenceCount--; if (!isOpen() && this.connection != null) { try { this.savepointCounter = 0; this.savepointsSupported = null; this.connection.close(); } catch (SQLException e) { throw new DataAccessException("cant not close connection.", e); } finally { this.connection = null; } } } public boolean isOpen() { if (referenceCount == 0) return false; return true; } /**获取连接*/ public synchronized Connection getConnection() throws SQLException { if (this.isOpen() == false) return null; if (this.connection == null) { this.connection = this.dataSource.getConnection(); } return this.connection; } /**是否存在事务*/ public boolean hasTransaction() throws SQLException { Connection conn = getConnection(); if (conn == null) return false; //AutoCommit被标记为 false 表示开启了事务。 return conn.getAutoCommit() == false ? true : false; } }
接下来这段代码展示了如何递交事务,回滚事务的处理方式亦是如此:
public final void commit(TransactionStatus status) throws SQLException { DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; /*已完毕,不需要处理*/ if (defStatus.isCompleted()) throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"); /*------------------------------------------------------------- | 1.无论何种传播形式,递交事务操作都会将 isCompleted 属性置为 true。 | 2.如果事务状态中包含一个未处理的保存点。仅递交保存点,而非递交整个事务。 | 3.事务 isNew 只有为 true 时才真正触发递交事务操作。 ===============================================================*/ try { prepareCommit(defStatus); if (defStatus.isReadOnly() || defStatus.isRollbackOnly()) { /*回滚情况*/ if (Hasor.isDebugLogger()) Hasor.logDebug("Transactional code has requested rollback"); doRollback(defStatus); } else { /*如果包含保存点,在递交事务时只处理保存点*/ if (defStatus.hasSavepoint()) defStatus.releaseHeldSavepoint(); else if (defStatus.isNewConnection()) doCommit(defStatus); } // } catch (SQLException ex) { doRollback(defStatus);/*递交失败,回滚*/ throw ex; } finally { cleanupAfterCompletion(defStatus); } }