從前面分析我們知道了sql的具體執行是通過調用SqlSession接口的對應的方法去執行的,而SqlSession最終都是通過調用了自己的Executor對象的query和update去執行的。本文就分析下sql的執行器-----Executor
Executor是mybatis的sql執行器,SqlSession是面向程序的,而Executor則就是面向數據庫的,先看下Executor接口的方法有哪些,源碼如下:
1 public interface Executor { 2 3 ResultHandler NO_RESULT_HANDLER = null; 4 5 int update(MappedStatement ms, Object parameter) throws SQLException; 6 7 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; 8 9 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; 10 11 <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; 12 13 List<BatchResult> flushStatements() throws SQLException; 14 15 void commit(boolean required) throws SQLException; 16 17 void rollback(boolean required) throws SQLException; 18 19 CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); 20 21 boolean isCached(MappedStatement ms, CacheKey key); 22 23 void clearLocalCache(); 24 25 void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType); 26 27 Transaction getTransaction(); 28 29 void close(boolean forceRollback); 30 31 boolean isClosed(); 32 33 void setExecutorWrapper(Executor executor);
和SqlSession一樣定義了各種各樣的sql執行的方法,有查詢的query方法,有更新的update方法,以及和事務有關的commit方法和rollback方法等,接下來就以query方法為例,看下具體是如何執行的。
Executor接口共有兩個實現類,分別是BaseExecutor和CachingExecutor,CachingExecutor是緩存執行器,后面會提到,現在先看下BaseExecutor
BaseExecutor的屬性有:
1 protected Transaction transaction;//事務 2 protected Executor wrapper;//執行器包裝者 3 4 protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//線程安全隊列 5 protected PerpetualCache localCache;//本地緩存 6 protected PerpetualCache localOutputParameterCache; 7 protected Configuration configuration; 8 9 protected int queryStack = 0;//查詢次數棧 10 private boolean closed;//是否已關閉(回滾的時候會被關閉)
再看下BaseExecutor執行query方法的源碼:
1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2 ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); 3 if (closed) {//判斷執行器是否已關閉 4 throw new ExecutorException("Executor was closed."); 5 } 6 //如果查詢次數棧為0並且MappedStatement可以清除緩存,則清除本地緩存 7 if (queryStack == 0 && ms.isFlushCacheRequired()) { 8 clearLocalCache(); 9 } 10 List<E> list; 11 try { 12 queryStack++;//查詢次數+1 13 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//從緩存中根據緩存key查詢是否有緩存 14 if (list != null) { 15 //如果緩存中有數據,則處理緩存 16 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); 17 } else { 18 //如果緩存中沒有數據,則從數據庫查詢數據 19 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); 20 } 21 } finally { 22 //查詢次數-1 23 queryStack--; 24 } 25 if (queryStack == 0) { 26 for (DeferredLoad deferredLoad : deferredLoads) { 27 deferredLoad.load(); 28 } 29 // issue #601 30 deferredLoads.clear(); 31 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { 32 // issue #482 33 clearLocalCache(); 34 } 35 } 36 return list; 37 }
可以看出BaseExecutor會優先從緩存中查詢數據,如果緩存不為空再從數據庫數據。在這里有一個queryStack會進行自增自減,它的作用是什么呢?
先看下如果沒有緩存的話,BaseExecutor是怎么從數據庫查詢數據的:
1 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2 List<E> list; 3 localCache.putObject(key, EXECUTION_PLACEHOLDER); 4 try { 5 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);//執行doQuery方法 6 } finally { 7 localCache.removeObject(key); 8 } 9 localCache.putObject(key, list);//將查詢結果放入緩存 10 if (ms.getStatementType() == StatementType.CALLABLE) {//如果callable類型查詢 11 localOutputParameterCache.putObject(key, parameter);//將參數放入緩存中 12 } 13 return list; 14 }
可見該方法是調用了doQuery方法從數據庫查詢了數據,然后將查詢的結果及查詢用的參數放入了緩存中,而doQuery方法是BaseExecutor中的抽象方法,具體的實現是由BaseExecutor的子類進行實現
1 protected abstract int doUpdate(MappedStatement ms, Object parameter) 2 throws SQLException; 3 4 protected abstract List<BatchResult> doFlushStatements(boolean isRollback) 5 throws SQLException; 6 7 protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) 8 throws SQLException; 9 10 protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) 11 throws SQLException; 12 13 protected void closeStatement(Statement statement) { 14 if (statement != null) { 15 try { 16 statement.close(); 17 } catch (SQLException e) { 18 // ignore 19 } 20 } 21 }
BaseExecutor共有SimpleExecutor、ReuseExecutor、BatchExecutor以及ClosedExecutor四個子類,這個后面再分析,現在我們以及知道了SqlSession是調用了Executor的方法來執行sql,而Executor的默認實現類的BaseExecutor,而BaseExecutor又是調用了其子類的方法。而BaseExecutor則對查詢的結果進行了緩存處理以及查詢的時候會從緩存中進行查詢。