public interface Executor { ResultHandler NO_RESULT_HANDLER = null; // 执行update,delete,insert三种类型的sql语句 int update(MappedStatement ms, Object parameter) throws SQLException; // 执行select类型的SQL语句,返回值分为结果对象列表和游标对象 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; // 批量执行SQL语句 List<BatchResult> flushStatements() throws SQLException; // 提交事务 void commit(boolean required) throws SQLException; // 事务回滚 void rollback(boolean required) throws SQLException; // 建立缓存中用到的CacheKey对象 CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); boolean isCached(MappedStatement ms, CacheKey key); // 清空一级缓存 void clearLocalCache(); // 延迟加载一级缓存中的数据 void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType); // 获取事务对象 Transaction getTransaction(); // 关闭Executor对象 void close(boolean forceRollback); // 检测Executor对象是否关闭 boolean isClosed(); void setExecutorWrapper(Executor executor); }
简单执行器SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完马上关闭Statement对象。(能够是Statement或PrepareStatement对象)sql
重用执行器ReuseExecutor:执行update或select,以sql做为key查找Statement对象,存在就使用,不存在就建立,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。(能够是Statement或PrepareStatement对象)数据库
批量执行器BatchExecutor:执行update(没有select,JDBC批处理不支持select),将全部sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每一个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor至关于维护了多个桶,每一个桶里都装了不少属于本身的SQL,就像苹果蓝里装了不少苹果,番茄蓝里装了不少番茄,最后,再统一倒进仓库。(能够是Statement或PrepareStatement对象)设计模式
缓存执行器CachingExecutor:装饰设计模式典范,先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate能够是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。缓存
无用执行器ClosedExecutor:毫无用处,读者可自行查看其源码,仅做为一种标识,和Serializable标记接口做用至关。app
做用范围:以上这五个执行器的做用范围,都严格限制在SqlSession生命周期范围内。ide
它是一个实现了Executor接口的抽象类,实现了接口中的大部分方法,其中就是使用了模板模式,它主要提供了缓存和事物管理的基本功能,不一样的实现类,只要实现4个基本方法来完成数据库的相关操做,这4个抽象方法:doUpdate()、doQuery()、doFlushStatement()、doQueryCursor。ui
源码片断this
protected Transaction transaction;// 实现事务的回滚和提交,关闭操做 protected Executor wrapper; // 其中封装的Executor对象 // 延迟加载队列 protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads; // 下面两个属性是一级缓存用到的对象 protected PerpetualCache localCache; protected PerpetualCache localOutputParameterCache; protected Configuration configuration; // 嵌套查询层级 protected int queryStack; private boolean closed;
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException; protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException; protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException; protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException;
@Override public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.update(stmt); } finally { closeStatement(stmt); } } @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt);//关闭statement对象 } }
源码很简单,从configuration对象中去材料,交给handler去处理,处理完后,statement对象立刻关闭。spa
执行器提供了Statement的重用功能,代码片断以下:设计
// 缓存使用过的Statement对象,key是SQL语句,value是SQL对应的Statement对象 private final Map<String, Statement> statementMap = new HashMap<String, Statement>();
/** * 准备获取Statement对象 * @param handler * @param statementLog * @return * @throws SQLException */ private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; BoundSql boundSql = handler.getBoundSql(); String sql = boundSql.getSql(); if (hasStatementFor(sql)) {// 检测是否缓存了相同模式的SQL语句所对应的Statement对象 stmt = getStatement(sql);// 从缓存中获取statement对象 applyTransactionTimeout(stmt);// 修改超时时间 } else { Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); putStatement(sql, stmt); } handler.parameterize(stmt); return stmt; }
那statement对象是何时关闭的呢?当事物提交回滚或者关闭时都须要关闭这些缓存的Statement对象,在BaseExecutor.commit(),rollback(),close()方法中都会掉用doFlushStatement()方法,因此在改方法中实现关闭Statement对象是很是合适。具体以下:
@Override public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException { // 遍历map集合并关闭其中的Statement对象 for (Statement stmt : statementMap.values()) { closeStatement(stmt); } // 清空缓存 statementMap.clear(); // 返回空集合 return Collections.emptyList(); }
BatchExecutor实现了批处理多条SQL语句的功能,须要注意的是在批处理执行SQL语句时,每次向数据库发送的SQL语句条数是有上限,超过上限会抛出异常,因此批量发送SQL语句的时机是很重要的。
其中的核心字段含义以下:
//缓存多个Statement对象,每一个Statement对象中都缓存了多条SQL语句 private final List<Statement> statementList = new ArrayList<Statement>(); //记录批处理的结果 BatchResult中经过updateCounts字段(int[])记录每一个Statement执行批处理的结果 private final List<BatchResult> batchResultList = new ArrayList<BatchResult>(); // 记录当前执行的SQL语句 private String currentSql; // 记录当前的MappedStatement对象 private MappedStatement currentStatement;
方法实现以下:
@Override public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException { final Configuration configuration = ms.getConfiguration(); final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null); final BoundSql boundSql = handler.getBoundSql(); // 本次执行的sql final String sql = boundSql.getSql(); final Statement stmt; // 若是当前执行的SQL与上一次执行的SQL相同且对应的MappedStatement对象相同 if (sql.equals(currentSql) && ms.equals(currentStatement)) { int last = statementList.size() - 1; // 已经存在Statement,取出最后一个Statement,有序 stmt = statementList.get(last); applyTransactionTimeout(stmt); handler.parameterize(stmt);//fix Issues 322 BatchResult batchResult = batchResultList.get(last); batchResult.addParameterObject(parameterObject); } else { // 尚不存在,新建Statement Connection connection = getConnection(ms.getStatementLog()); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); //fix Issues 322 currentSql = sql; currentStatement = ms; // 放到statementList缓存 statementList.add(stmt); batchResultList.add(new BatchResult(ms, sql, parameterObject)); } // handler.parameterize(stmt); // 将sql以addBatch()的方式,添加到Statement中(该步骤由StatementHandler内部完成) handler.batch(stmt); return BATCH_UPDATE_RETURN_VALUE; }
须要注意的是sql.equals(currentSql)和statementList.get(last),充分说明了其有序逻辑:AABB,将生成2个Statement对象;AABBAA,将生成3个Statement对象,而不是2个。由于,只要sql有变化,将致使生成新的Statement对象。
缓存了这么多Statement批处理对象,什么时候执行它们?在doFlushStatements()方法中完成执行stmt.executeBatch(),随即关闭这些Statement对象。
CachingExecutor是一个Executor接口的装饰器,它为Executor对象增长了二级缓存的相关功能。
//委托的执行器对象,能够是SimpleExecutor、ReuseExecutor、BatchExecutor任一一个 private final Executor delegate; //管理使用的二级缓存对像 private final TransactionalCacheManager tcm = new TransactionalCacheManager();
query方法执行的查询操做步骤:
(1)获取BoundSql对象,建立查询语句对应的CacheKey对象,
(2)检测是否开启了二级缓存,若是没有,则指教调用delegate对象的query()方法查询,若是开启了,则继续后面的步骤
(3)检测查询是否包含输出类型的参数,若是是,则报错
(4)调用TransactionalCacheManager.getObject()方法查询二级缓存,若是二级缓存中查找到相应的结果,则直接返回结果。
(5)若是二级缓存没有相应的结果对象,在调用delegate对象的query()方法查询。最后将获得的结果放入
TransactionalCache.entriesToAddOnCommit集合中保存。
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // (1) BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) {// (2) flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { // (3) ensureNoOutParams(ms, boundSql); // (4) @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) {// (5) list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // 没有启动二级缓存,只调用底层Executor查询 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }