一文詳解mybatis二級(jí)緩存執(zhí)行流程
mybatis二級(jí)緩存的執(zhí)行流程
1.二級(jí)緩存的生成
在mybatis啟動(dòng)時(shí)會(huì)加載并解析配置文件,其中就會(huì)解析二級(jí)緩存的可選項(xiàng)配置,由此是否生成二級(jí)緩存,下面這段代碼就是解析xxxMapper.xml文件中的子標(biāo)簽來生成二級(jí)緩存
//XMLmapperBuilder private void configurationElement(XNode context) { try { // 獲取<mapper>標(biāo)簽的namespace值,也就是命名空間 String namespace = context.getStringAttribute("namespace"); // 命名空間不能為空 if (namespace == null || namespace.isEmpty()) { throw new BuilderException("Mapper's namespace cannot be empty"); } // MapperBuilderAssistant:構(gòu)建MappedStatement對(duì)象的構(gòu)建助手,設(shè)置當(dāng)前的命名空間為namespace的值 builderAssistant.setCurrentNamespace(namespace); ...... // 解析<cache>子標(biāo)簽,這里生成二級(jí)緩存 cacheElement(context.evalNode("cache")); ...... } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }
默認(rèn)的二級(jí)緩存對(duì)象是PerpetuateCache,并把緩存封裝進(jìn)MapperStatement,所以一個(gè)MapperStatement對(duì)應(yīng)一個(gè)二級(jí)緩存,一個(gè)MapperStatement就是存儲(chǔ)一個(gè)xxxMapper.xml文件中的信息
//XMLmapperBuilder private void cacheElement(XNode context) { if (context != null) { // 解析<cache>標(biāo)簽type屬性的值,在這可以自定義type的值,比如redisCache,如果沒有指定默認(rèn)就是PERPETUAL String type = context.getStringAttribute("type", "PERPETUAL"); Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); // 獲取負(fù)責(zé)過期的eviction對(duì)象,默認(rèn)策略為L(zhǎng)RU String eviction = context.getStringAttribute("eviction", "LRU"); Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); // 清空緩存的頻率 0代表不清空 Long flushInterval = context.getLongAttribute("flushInterval"); // 緩存容器的大小 Integer size = context.getIntAttribute("size"); // 是否只讀 boolean readWrite = !context.getBooleanAttribute("readOnly", false); // 是否阻塞 boolean blocking = context.getBooleanAttribute("blocking", false); // 獲得Properties屬性 Properties props = context.getChildrenAsProperties(); //builderAssistant是MapperBuilderAssistant,作用就是解耦建立MapperStatement,因?yàn)? //MapperStatement對(duì)象創(chuàng)建復(fù)雜,所以用這個(gè)類來解耦創(chuàng)建 builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } }
PerpetualCache內(nèi)部的緩存其實(shí)就是個(gè)HashMap
public class PerpetualCache implements Cache { private final String id; private final Map<Object, Object> cache = new HashMap<>();
2.二級(jí)緩存的使用
在執(zhí)行查詢時(shí),會(huì)先走二級(jí)緩存,若二級(jí)緩存沒有才走下一步查詢,并把查詢結(jié)果存到二級(jí)緩存中,但此時(shí)只是存到tcm(TransactionalCacheManager)中的一個(gè)map中
//CachingExecutor @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // 獲取二級(jí)緩存,是從MapperStatement里面獲取的 Cache cache = ms.getCache(); if (cache != null) { // 刷新tcm的緩存 (存在緩存且flushCache為true時(shí)) flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") // 從二級(jí)緩存中查詢數(shù)據(jù) List<E> list = (List<E>) tcm.getObject(cache, key); // 如果二級(jí)緩存中沒有查詢到數(shù)據(jù),則查詢一級(jí)緩存及數(shù)據(jù)庫(kù) if (list == null) { // 委托給BaseExecutor執(zhí)行 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 將查詢結(jié)果 要存到二級(jí)緩存中(注意:此處只是存到map集合中,沒有真正存到二級(jí)緩存中) tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // 如果沒有開啟二級(jí)緩存或開啟了沒查到二級(jí)緩存則委托給BaseExecutor執(zhí)行下一步查詢 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
TransactionalCacheManager顧名思義事務(wù)緩存管理器,只在一次事務(wù)中進(jìn)行緩存管理,當(dāng)事務(wù)commit后tcm就不存在了。在一次事務(wù)commit前可以進(jìn)行多次數(shù)據(jù)庫(kù)操作,例如進(jìn)行2次查詢。
public class TransactionalCacheManager { // Cache 與 TransactionalCache 的映射關(guān)系表 private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>(); public Object getObject(Cache cache, CacheKey key) { // 直接從TransactionalCache中獲取緩存 return getTransactionalCache(cache).getObject(key); } public void putObject(Cache cache, CacheKey key, Object value) { // 直接存入TransactionalCache的緩存中 getTransactionalCache(cache).putObject(key, value); } private TransactionalCache getTransactionalCache(Cache cache) { // 從映射表中獲取 TransactionalCache,下面代碼等同于 // transactionalCaches.computeIfAbsent(cache, TransactionalCache::new); return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new); }
上面提到查詢時(shí)先走二級(jí)緩存,二級(jí)緩存查詢從這開始,從這里查一下二級(jí)緩存是否存在,不存在再走其他查詢結(jié)果,以下的delegate就是MapperStatement的二級(jí)緩存對(duì)象
public class TransactionalCache implements Cache { /* 二級(jí)緩存 Cache 對(duì)象。 */ private final Cache delegate; /** * 提交時(shí),清空 {@link #delegate} * * 初始時(shí),該值為 false * 清理后{@link #clear()} 時(shí),該值為 true ,表示持續(xù)處于清空狀態(tài) */ private boolean clearOnCommit; /** * // 在事務(wù)被提交前,所有從數(shù)據(jù)庫(kù)中查詢的結(jié)果將緩存在此集合中 */ private final Map<Object, Object> entriesToAddOnCommit; /** * 在事務(wù)被提交前,當(dāng)緩存未命中時(shí),CacheKey 將會(huì)被存儲(chǔ)在此集合中 */ private final Set<Object> entriesMissedInCache; public Object getObject(Object key) { // issue #116 // 查詢的時(shí)候是直接從delegate(就是MapperSatement的二級(jí)緩存對(duì)象)中去查詢的 Object object = delegate.getObject(key); // 如果不存在,則添加到 entriesMissedInCache 中 if (object == null) { // 緩存未命中,則將 key 存入到 entriesMissedInCache 中 entriesMissedInCache.add(key); } // issue #146 // 如果 clearOnCommit 為 true ,表示處于持續(xù)清空狀態(tài),則返回 null if (clearOnCommit) { return null; } else { // 返回 value return object; } }
不存在二級(jí)緩存,走其他查詢后,把查詢結(jié)果放進(jìn)二級(jí)緩存中,但這里其實(shí)并沒放進(jìn)二級(jí)緩存,而是放到了entriesToAddOnCommit中,畢竟一次事務(wù)之后tcm才不存在,所以在事務(wù)commit后,再放進(jìn)真正二級(jí)緩存
//TransactionalCache public void putObject(Object key, Object object) { // 將鍵值對(duì)存入到 entriesToAddOnCommit 這個(gè)Map中中,而非真實(shí)的緩存對(duì)象 delegate 中 entriesToAddOnCommit.put(key, object); }
當(dāng)一次事務(wù)commit后,會(huì)把查詢結(jié)果真正放到二級(jí)緩存中
public void commit() { // 如果 clearOnCommit 為 true ,則清空 delegate 緩存 if (clearOnCommit) { delegate.clear(); } // 將 entriesToAddOnCommit、entriesMissedInCache 刷入 delegate(cache) 中 flushPendingEntries(); // 重置 reset(); } private void flushPendingEntries() { // 將 entriesToAddOnCommit 中的內(nèi)容轉(zhuǎn)存到 delegate 中 for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) { // 在這里真正的將entriesToAddOnCommit的對(duì)象逐個(gè)添加到delegate中,只有這時(shí),二級(jí)緩存才真正的生效 delegate.putObject(entry.getKey(), entry.getValue()); } // 將 entriesMissedInCache 刷入 delegate 中 for (Object entry : entriesMissedInCache) { if (!entriesToAddOnCommit.containsKey(entry)) { delegate.putObject(entry, null); } } }
到此這篇關(guān)于一文詳解mybatis二級(jí)緩存執(zhí)行流程的文章就介紹到這了,更多相關(guān)mybatis二級(jí)緩存執(zhí)行內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
hibernate-validator后端表單數(shù)據(jù)校驗(yàn)的使用示例詳解
這篇文章主要介紹了hibernate-validator后端表單數(shù)據(jù)校驗(yàn)的使用,hibernate-validator提供的校驗(yàn)方式為在類的屬性上加入相應(yīng)的注解來達(dá)到校驗(yàn)的目的,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08SpringBoot集成JPA持久層框架,簡(jiǎn)化數(shù)據(jù)庫(kù)操作
JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化規(guī)范。主要是為了簡(jiǎn)化持久層開發(fā)以及整合ORM技術(shù),結(jié)束Hibernate、TopLink、JDO等ORM框架各自為營(yíng)的局面。JPA是在吸收現(xiàn)有ORM框架的基礎(chǔ)上發(fā)展而來,易于使用,伸縮性強(qiáng)。2021-06-06基于Mybatis-plus實(shí)現(xiàn)多租戶架構(gòu)的全過程
多租戶是一種軟件架構(gòu)技術(shù),在多用戶的環(huán)境下,共有同一套系統(tǒng),并且要注意數(shù)據(jù)之間的隔離性,下面這篇文章主要給大家介紹了關(guān)于基于Mybatis-plus實(shí)現(xiàn)多租戶架構(gòu)的相關(guān)資料,需要的朋友可以參考下2022-02-02Spring實(shí)現(xiàn)內(nèi)置監(jiān)聽器
這篇文章主要介紹了Spring 實(shí)現(xiàn)自定義監(jiān)聽器案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧,希望能給你帶來幫助2021-07-07Java rmi遠(yuǎn)程方法調(diào)用基本用法解析
這篇文章主要介紹了Java rmi遠(yuǎn)程方法調(diào)用基本用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05SpringBoot集成ElaticJob定時(shí)器的實(shí)現(xiàn)代碼
這篇文章主要介紹了SpringBoot集成ElaticJob定時(shí)器的實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06Jenkin郵件收發(fā)實(shí)現(xiàn)原理及過程詳解
這篇文章主要介紹了Jenkin郵件收發(fā)實(shí)現(xiàn)原理及過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09IDEA版使用Java操作Redis數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了IDEA版使用Java操作Redis數(shù)據(jù)庫(kù)的方法,首先需要下載jedis.jar包,然后再工程中設(shè)置具體操作步驟跟隨小編一起學(xué)習(xí)下吧2021-08-08