實習日誌(3)mybaits1級快取和2級快取
阿新 • • 發佈:2018-12-09
從<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級別