1. 程式人生 > >實習日誌(3)mybaits1級快取和2級快取

實習日誌(3)mybaits1級快取和2級快取

從<cache>和<cache-ref>說起

一級快取

一級快取預設是開啟的,是由baseExecutor來 維護,預設級別是session級別

 protected PerpetualCache localCache;
至於PerpetualCache只是 個最簡單的使用hashmap來實現,有點意思事cacheKey的生成
是由當前statement id (即xml 裡的namespace + sql語句 的id + rowbound邏輯分頁的範圍 + sql + 引數 進行一系列運算獲取 到的值)
public class PerpetualCache implements Cache {

  private final String id;

  private Map<Object, Object> cache = new HashMap<Object, Object>();


 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;
      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) { // 如果 快取級別設定成statement,查詢結束之後就 清空快取
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

二級快取的建立

開啟必須配置xml檔案增加<cache> 或者<cache-ref>標籤
在解析mapper.xml開始,裡面解析是 很重要的,

  private void cacheElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type", "PERPETUAL");
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      String eviction = context.getStringAttribute("eviction", "LRU");
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      Long flushInterval = context.getLongAttribute("flushInterval");
      Integer size = context.getIntAttribute("size");
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); // 建立新的快取,竊以為有了cache-ref
      // 就不需要cache了
    }

  }
最終呼叫 configuration   的 
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  public void addCache(Cache cache) {
    caches.put(cache.getId(), cache);
  }
  cache的id即namespace,意思是一個 名稱空間都可以由一份cache

二級 快取的使用

當 開始<cache>的時候建立的是cachingExector 裝飾類,

  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) {
      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);
  }
MappedStatement下都 維護者  private Cache cache;物件 
在 mapper 掃描的時候就 已經把cache物件傳 入其中了,因此,executor會先判斷當前namespace有無可用的快取,然後再交給simpleExecutor判斷是否由快取


總結

二級快取是application級別,一級快取預設是session級別