mybatis一級緩存與二級緩存的原理


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

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM