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); }