1.mybatis中的緩存是在mybatis框架中的Executor中來實現的,我們來看一下Executor的繼承圖

2.通過以上類圖我們可以發現Executor接口下有兩大實現類BaseExecutor與CachingExecutor
(1)BaseExecutor(用來存儲我們的一級緩存)
@Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; //會先去localCache中去查找我們的數據 if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); //如不存在則會去數據庫中查找 } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; }
(2)CachingExecutor(是裝飾器模式的實現,用來查詢我們的二級緩存)
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); //在MappedStatement中去獲取二級緩存的類型。 if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); //通過tcm來查詢緩存,而tcm是CachingExecutor中的變量TransactionalCacheManager if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //delegate為CachingExecutor中保存的BaseExecutor的引用,若二級緩存不存在回去調用BaseExecutor的方法。 tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } }
3.當我們開啟二級緩存后Mybatis會使用CachingExecutor去裝飾我們的BaseExecutor,所以會先查詢二級緩存后再去查詢一級緩存。
Configuration中的newExecutor方法
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { //判斷Executor的類型 executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { //如果二級緩存開啟則使用CachingExecutor去裝飾我們的executor; executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
4.一級緩存與二級緩存的作用范圍
(1)一級緩存(由於一級緩存是存在BaseExecutor中的,而Executor又作為創建SqlSession的參數,因此一級緩存具有和sqlsession一樣的生命周期)
這是在SqlSessionFactory中調用openSession()中調用的方法
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); //新建一個Executor作為參數傳給DefaultSqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
(2)二級緩存(使用CachingExecutor來裝飾)
//CachingExecutor中的query方法
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); //由此可見我們的二級緩存並不是儲存在CachingExecutor中的,而是從MappedStatement中去獲取。因此mybatis的二級緩存的生命周期為mapper級別的 if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
