日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

带你彻底搞懂MyBatis的底层实现之缓存模块(Cache)-吊打面试官必备技能

發(fā)布時(shí)間:2023/12/14 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 带你彻底搞懂MyBatis的底层实现之缓存模块(Cache)-吊打面试官必备技能 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

??基礎(chǔ)支持層位于MyBatis整體架構(gòu)的最底層,支撐著MyBatis的核心處理層,是整個(gè)框架的基石。基礎(chǔ)支持層中封裝了多個(gè)較為通用的、獨(dú)立的模塊。不僅僅為MyBatis提供基礎(chǔ)支撐,也可以在合適的場景中直接復(fù)用。

??上篇文章我們給大家聊了下binding模塊,本篇文章我們重點(diǎn)來聊下緩存(Cache)模塊。

緩存模塊

??MyBatis作為一個(gè)強(qiáng)大的持久層框架,緩存是其必不可少的功能之一,Mybatis中的緩存分為一級緩存和二級緩存。但本質(zhì)上是一樣的,都是使用Cache接口實(shí)現(xiàn)的。緩存位于 org.apache.ibatis.cache包下。

??通過結(jié)構(gòu)我們能夠發(fā)現(xiàn)Cache其實(shí)使用到了裝飾器模式來實(shí)現(xiàn)緩存的處理。首先大家需要先回顧下裝飾器模式的相關(guān)內(nèi)容哦。我們先來看看Cache中的基礎(chǔ)類的API

// 煎餅加雞蛋加香腸
“裝飾者模式(Decorator Pattern)是指在不改變原有對象的基礎(chǔ)之上,將功能附加到對象上,提供了比繼承更有彈性的替代方案(擴(kuò)展原有對象的功能)。”

1 Cache接口

??Cache接口是緩存模塊中最核心的接口,它定義了所有緩存的基本行為,Cache接口的定義如下:

public interface Cache {/*** 緩存對象的 ID* @return The identifier of this cache*/String getId();/*** 向緩存中添加數(shù)據(jù),一般情況下 key是CacheKey value是查詢結(jié)果* @param key Can be any object but usually it is a {@link CacheKey}* @param value The result of a select.*/void putObject(Object key, Object value);/*** 根據(jù)指定的key,在緩存中查找對應(yīng)的結(jié)果對象* @param key The key* @return The object stored in the cache.*/Object getObject(Object key);/*** As of 3.3.0 this method is only called during a rollback* for any previous value that was missing in the cache.* This lets any blocking cache to release the lock that* may have previously put on the key.* A blocking cache puts a lock when a value is null* and releases it when the value is back again.* This way other threads will wait for the value to be* available instead of hitting the database.* 刪除key對應(yīng)的緩存數(shù)據(jù)** @param key The key* @return Not used*/Object removeObject(Object key);/*** Clears this cache instance.* 清空緩存*/void clear();/*** Optional. This method is not called by the core.* 緩存的個(gè)數(shù)。* @return The number of elements stored in the cache (not its capacity).*/int getSize();/*** Optional. As of 3.2.6 this method is no longer called by the core.* <p>* Any locking needed by the cache must be provided internally by the cache provider.* 獲取讀寫鎖* @return A ReadWriteLock*/default ReadWriteLock getReadWriteLock() {return null;}}

??Cache接口的實(shí)現(xiàn)類很多,但是大部分都是裝飾器,只有PerpetualCache提供了Cache接口的基本實(shí)現(xiàn)。

2 PerpetualCache

??PerpetualCache在緩存模塊中扮演了ConcreteComponent的角色,其實(shí)現(xiàn)比較簡單,底層使用HashMap記錄緩存項(xiàng),具體的實(shí)現(xiàn)如下:

/*** 在裝飾器模式用 用來被裝飾的對象* 緩存中的 基本緩存處理的實(shí)現(xiàn)* 其實(shí)就是一個(gè) HashMap 的基本操作* @author Clinton Begin*/ public class PerpetualCache implements Cache {private final String id; // Cache 對象的唯一標(biāo)識// 用于記錄緩存的Map對象private final Map<Object, Object> cache = new HashMap<>();public PerpetualCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic int getSize() {return cache.size();}@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}@Overridepublic Object removeObject(Object key) {return cache.remove(key);}@Overridepublic void clear() {cache.clear();}@Overridepublic boolean equals(Object o) {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}if (this == o) {return true;}if (!(o instanceof Cache)) {return false;}Cache otherCache = (Cache) o;// 只關(guān)心IDreturn getId().equals(otherCache.getId());}@Overridepublic int hashCode() {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}// 只關(guān)心IDreturn getId().hashCode();}}

??然后我們可以來看看cache.decorators包下提供的裝飾器。他們都實(shí)現(xiàn)了Cache接口。這些裝飾器都在PerpetualCache的基礎(chǔ)上提供了一些額外的功能,通過多個(gè)組合實(shí)現(xiàn)一些特殊的需求。

3 BlockingCache

??通過名稱我們能看出來是一個(gè)阻塞同步的緩存,它保證只有一個(gè)線程到緩存中查找指定的key對應(yīng)的數(shù)據(jù)。

public class BlockingCache implements Cache {private long timeout; // 阻塞超時(shí)時(shí)長private final Cache delegate; // 被裝飾的底層 Cache 對象// 每個(gè)key 都有對象的 ReentrantLock 對象private final ConcurrentHashMap<Object, ReentrantLock> locks;public BlockingCache(Cache delegate) {// 被裝飾的 Cache 對象this.delegate = delegate;this.locks = new ConcurrentHashMap<>();}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}@Overridepublic void putObject(Object key, Object value) {try {// 執(zhí)行 被裝飾的 Cache 中的方法delegate.putObject(key, value);} finally {// 釋放鎖releaseLock(key);}}@Overridepublic Object getObject(Object key) {acquireLock(key); // 獲取鎖Object value = delegate.getObject(key); // 獲取緩存數(shù)據(jù)if (value != null) { // 有數(shù)據(jù)就釋放掉鎖,否則繼續(xù)持有鎖releaseLock(key);}return value;}@Overridepublic Object removeObject(Object key) {// despite of its name, this method is called only to release locksreleaseLock(key);return null;}@Overridepublic void clear() {delegate.clear();}private ReentrantLock getLockForKey(Object key) {return locks.computeIfAbsent(key, k -> new ReentrantLock());}private void acquireLock(Object key) {Lock lock = getLockForKey(key);if (timeout > 0) {try {boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);if (!acquired) {throw new CacheException("Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());}} catch (InterruptedException e) {throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);}} else {lock.lock();}}private void releaseLock(Object key) {ReentrantLock lock = locks.get(key);if (lock.isHeldByCurrentThread()) {lock.unlock();}}public long getTimeout() {return timeout;}public void setTimeout(long timeout) {this.timeout = timeout;} }

??通過源碼我們能夠發(fā)現(xiàn),BlockingCache本質(zhì)上就是在我們操作緩存數(shù)據(jù)的前后通過 ReentrantLock對象來實(shí)現(xiàn)了加鎖和解鎖操作。其他的具體實(shí)現(xiàn)類,大家可以自行查閱

緩存實(shí)現(xiàn)類描述作用裝飾條件
基本緩存緩存基本實(shí)現(xiàn)類默認(rèn)是PerpetualCache,也可以自定義比如RedisCache、EhCache等,具備基本功能的緩存類
LruCacheLRU策略的緩存當(dāng)緩存到達(dá)上限時(shí)候,刪除最近最少使用的緩存(Least Recently Use)eviction=“LRU”(默認(rèn))
FifoCacheFIFO策略的緩存當(dāng)緩存到達(dá)上限時(shí)候,刪除最先入隊(duì)的緩存eviction=“FIFO”
SoftCacheWeakCache帶清理策略的緩存通過JVM的軟引用和弱引用來實(shí)現(xiàn)緩存,當(dāng)JVM內(nèi)存不足時(shí),會(huì)自動(dòng)清理掉這些緩存,基于SoftReference和WeakReferenceeviction="SOFT"eviction=“WEAK”
LoggingCache帶日志功能的緩存比如:輸出緩存命中率基本
SynchronizedCache同步緩存基于synchronized關(guān)鍵字實(shí)現(xiàn),解決并發(fā)問題基本
BlockingCache阻塞緩存通過在get/put方式中加鎖,保證只有一個(gè)線程操作緩存,基于Java重入鎖實(shí)現(xiàn)blocking=true
SerializedCache支持序列化的緩存將對象序列化以后存到緩存中,取出時(shí)反序列化readOnly=false(默認(rèn))
ScheduledCache定時(shí)調(diào)度的緩存在進(jìn)行g(shù)et/put/remove/getSize等操作前,判斷緩存時(shí)間是否超過了設(shè)置的最長緩存時(shí)間(默認(rèn)是一小時(shí)),如果是則清空緩存–即每隔一段時(shí)間清空一次緩存flushInterval不為空
TransactionalCache事務(wù)緩存在二級緩存中使用,可一次存入多個(gè)緩存,移除多個(gè)緩存在TransactionalCacheManager中用Map維護(hù)對應(yīng)關(guān)系

4 緩存的應(yīng)用

4.1 緩存對應(yīng)的初始化

??在Configuration初始化的時(shí)候會(huì)為我們的各種Cache實(shí)現(xiàn)注冊對應(yīng)的別名

在解析settings標(biāo)簽的時(shí)候,設(shè)置的默認(rèn)值有如下

cacheEnabled默認(rèn)為true,localCacheScope默認(rèn)為 SESSION

在解析映射文件的時(shí)候會(huì)解析我們相關(guān)的cache標(biāo)簽

然后解析映射文件的cache標(biāo)簽后會(huì)在Configuration對象中添加對應(yīng)的數(shù)據(jù)在

private void cacheElement(XNode context) {// 只有 cache 標(biāo)簽不為空才解析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);}}

繼續(xù)
然后我們可以發(fā)現(xiàn) 如果存儲(chǔ) cache 標(biāo)簽,那么對應(yīng)的 Cache對象會(huì)被保存在 currentCache 屬性中。

進(jìn)而在 Cache 對象 保存在了 MapperStatement 對象的 cache 屬性中。

然后我們再看看openSession的時(shí)候又做了哪些操作,在創(chuàng)建對應(yīng)的執(zhí)行器的時(shí)候會(huì)有緩存的操作

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 = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {// 默認(rèn) SimpleExecutorexecutor = new SimpleExecutor(this, transaction);}// 二級緩存開關(guān),settings 中的 cacheEnabled 默認(rèn)是 trueif (cacheEnabled) {executor = new CachingExecutor(executor);}// 植入插件的邏輯,至此,四大對象已經(jīng)全部攔截完畢executor = (Executor) interceptorChain.pluginAll(executor);return executor;}

??也就是如果 cacheEnabled 為 true 就會(huì)通過 CachingExecutor 來裝飾executor 對象,然后就是在執(zhí)行SQL操作的時(shí)候會(huì)涉及到緩存的具體使用。這個(gè)就分為一級緩存和二級緩存,這個(gè)我們來分別介紹

4.2 一級緩存

??一級緩存也叫本地緩存(Local Cache),MyBatis的一級緩存是在會(huì)話(SqlSession)層面進(jìn)行緩存的。MyBatis的一級緩存是默認(rèn)開啟的,不需要任何的配置(如果要關(guān)閉,localCacheScope設(shè)置為STATEMENT)。在BaseExecutor對象的query方法中有關(guān)閉一級緩存的邏輯

??然后我們需要考慮下在一級緩存中的 PerpetualCache 對象在哪創(chuàng)建的,因?yàn)橐患壘彺媸荢ession級別的緩存,肯定需要在Session范圍內(nèi)創(chuàng)建,其實(shí)PerpetualCache的實(shí)例化是在BaseExecutor的構(gòu)造方法中創(chuàng)建的

protected BaseExecutor(Configuration configuration, Transaction transaction) {this.transaction = transaction;this.deferredLoads = new ConcurrentLinkedQueue<>();this.localCache = new PerpetualCache("LocalCache");this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");this.closed = false;this.configuration = configuration;this.wrapper = this;}

??一級緩存的具體實(shí)現(xiàn)也是在BaseExecutor的query方法中來實(shí)現(xiàn)的

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {// 異常體系之 ErrorContextErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {// flushCache="true"時(shí),即使是查詢,也清空一級緩存clearLocalCache();}List<E> list;try {// 防止遞歸查詢重復(fù)處理緩存queryStack++;// 查詢一級緩存// ResultHandler 和 ResultSetHandler的區(qū)別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 #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}

一級緩存的驗(yàn)證:

同一個(gè)Session中的多個(gè)相同操作

@Testpublic void test1() throws Exception{// 1.獲取配置文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");// 2.加載解析配置文件并獲取SqlSessionFactory對象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);// 3.根據(jù)SqlSessionFactory對象獲取SqlSession對象SqlSession sqlSession = factory.openSession();// 4.通過SqlSession中提供的 API方法來操作數(shù)據(jù)庫List<User> list = sqlSession.selectList("com.gupaoedu.mapper.UserMapper.selectUserList");System.out.println(list.size());// 一級緩存測試System.out.println("---------");list = sqlSession.selectList("com.gupaoedu.mapper.UserMapper.selectUserList");System.out.println(list.size());// 5.關(guān)閉會(huì)話sqlSession.close();}

輸出日志

Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@477b4cdf] ==> Preparing: select * from t_user ==> Parameters: <== Columns: id, user_name, real_name, password, age, d_id <== Row: 1, zhangsan, 張三, 123456, 18, null <== Row: 2, lisi, 李四, 11111, 19, null <== Row: 3, wangwu, 王五, 111, 22, 1001 <== Row: 4, wangwu, 王五, 111, 22, 1001 <== Row: 5, wangwu, 王五, 111, 22, 1001 <== Row: 6, wangwu, 王五, 111, 22, 1001 <== Row: 7, wangwu, 王五, 111, 22, 1001 <== Row: 8, aaa, bbbb, null, null, null <== Row: 9, aaa, bbbb, null, null, null <== Row: 10, aaa, bbbb, null, null, null <== Row: 11, aaa, bbbb, null, null, null <== Row: 12, aaa, bbbb, null, null, null <== Row: 666, hibernate, 持久層框架, null, null, null <== Total: 13 13 --------- 13

可以看到第二次查詢沒有經(jīng)過數(shù)據(jù)庫操作

不同Session的相同操作

@Testpublic void test2() throws Exception{// 1.獲取配置文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");// 2.加載解析配置文件并獲取SqlSessionFactory對象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);// 3.根據(jù)SqlSessionFactory對象獲取SqlSession對象SqlSession sqlSession = factory.openSession();// 4.通過SqlSession中提供的 API方法來操作數(shù)據(jù)庫List<User> list = sqlSession.selectList("com.gupaoedu.mapper.UserMapper.selectUserList");System.out.println(list.size());sqlSession.close();sqlSession = factory.openSession();// 一級緩存測試System.out.println("---------");list = sqlSession.selectList("com.gupaoedu.mapper.UserMapper.selectUserList");System.out.println(list.size());// 5.關(guān)閉會(huì)話sqlSession.close();}

輸出結(jié)果

Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@477b4cdf] ==> Preparing: select * from t_user ==> Parameters: <== Columns: id, user_name, real_name, password, age, d_id <== Row: 1, zhangsan, 張三, 123456, 18, null <== Row: 2, lisi, 李四, 11111, 19, null <== Row: 3, wangwu, 王五, 111, 22, 1001 <== Row: 4, wangwu, 王五, 111, 22, 1001 <== Row: 5, wangwu, 王五, 111, 22, 1001 <== Row: 6, wangwu, 王五, 111, 22, 1001 <== Row: 7, wangwu, 王五, 111, 22, 1001 <== Row: 8, aaa, bbbb, null, null, null <== Row: 9, aaa, bbbb, null, null, null <== Row: 10, aaa, bbbb, null, null, null <== Row: 11, aaa, bbbb, null, null, null <== Row: 12, aaa, bbbb, null, null, null <== Row: 666, hibernate, 持久層框架, null, null, null <== Total: 13 13 Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@477b4cdf] Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@477b4cdf] Returned connection 1199262943 to pool. --------- Opening JDBC Connection Checked out connection 1199262943 from pool. Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@477b4cdf] ==> Preparing: select * from t_user ==> Parameters: <== Columns: id, user_name, real_name, password, age, d_id <== Row: 1, zhangsan, 張三, 123456, 18, null <== Row: 2, lisi, 李四, 11111, 19, null <== Row: 3, wangwu, 王五, 111, 22, 1001 <== Row: 4, wangwu, 王五, 111, 22, 1001 <== Row: 5, wangwu, 王五, 111, 22, 1001 <== Row: 6, wangwu, 王五, 111, 22, 1001 <== Row: 7, wangwu, 王五, 111, 22, 1001 <== Row: 8, aaa, bbbb, null, null, null <== Row: 9, aaa, bbbb, null, null, null <== Row: 10, aaa, bbbb, null, null, null <== Row: 11, aaa, bbbb, null, null, null <== Row: 12, aaa, bbbb, null, null, null <== Row: 666, hibernate, 持久層框架, null, null, null <== Total: 13 13 Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@477b4cdf] Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@477b4cdf] Returned connection 1199262943 to pool.

通過輸出我們能夠發(fā)現(xiàn),不同的Session中的相同操作,一級緩存是沒有起作用的。

4.3 二級緩存

??二級緩存是用來解決一級緩存不能跨會(huì)話共享的問題的,范圍是namespace級別的,可以被多個(gè)SqlSession共享(只要是同一個(gè)接口里面的相同方法,都可以共享),生命周期和應(yīng)用同步。

??二級緩存的設(shè)置,首先是settings中的cacheEnabled要設(shè)置為true,當(dāng)然默認(rèn)的就是為true,這個(gè)步驟決定了在創(chuàng)建Executor對象的時(shí)候是否通過CachingExecutor來裝飾。

??那么設(shè)置了cacheEnabled標(biāo)簽為true是否就意味著 二級緩存是否一定可用呢?當(dāng)然不是,我們還需要在 對應(yīng)的映射文件中添加 cache 標(biāo)簽才行。

<!-- 聲明這個(gè)namespace使用二級緩存 --> <cache type="org.apache.ibatis.cache.impl.PerpetualCache"size="1024" <!—最多緩存對象個(gè)數(shù),默認(rèn)1024-->eviction="LRU" <!—回收策略-->flushInterval="120000" <!—自動(dòng)刷新時(shí)間 ms,未配置時(shí)只有調(diào)用時(shí)刷新-->readOnly="false"/> <!—默認(rèn)是false(安全),改為true可讀寫時(shí),對象必須支持序列化 -->

cache屬性詳解:

屬性含義取值
type緩存實(shí)現(xiàn)類需要實(shí)現(xiàn)Cache接口,默認(rèn)是PerpetualCache,可以使用第三方緩存
size最多緩存對象個(gè)數(shù)默認(rèn)1024
eviction回收策略(緩存淘汰算法)LRU – 最近最少使用的:移除最長時(shí)間不被使用的對象(默認(rèn))。FIFO – 先進(jìn)先出:按對象進(jìn)入緩存的順序來移除它們。SOFT – 軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象。WEAK – 弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
flushInterval定時(shí)自動(dòng)清空緩存間隔自動(dòng)刷新時(shí)間,單位 ms,未配置時(shí)只有調(diào)用時(shí)刷新
readOnly是否只讀true:只讀緩存;會(huì)給所有調(diào)用者返回緩存對象的相同實(shí)例。因此這些對象不能被修改。這提供了很重要的性能優(yōu)勢。false:讀寫緩存;會(huì)返回緩存對象的拷貝(通過序列化),不會(huì)共享。這會(huì)慢一些,但是安全,因此默認(rèn)是 false。改為false可讀寫時(shí),對象必須支持序列化。
blocking啟用阻塞緩存通過在get/put方式中加鎖,保證只有一個(gè)線程操作緩存,基于Java重入鎖實(shí)現(xiàn)

再來看下cache標(biāo)簽在源碼中的體現(xiàn),創(chuàng)建cacheKey

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 獲取SQLBoundSql boundSql = ms.getBoundSql(parameterObject);// 創(chuàng)建CacheKey:什么樣的SQL是同一條SQL? >>CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

createCacheKey自行進(jìn)去查看


而這看到的和我們前面在緩存初始化時(shí)看到的 cache 標(biāo)簽解析操作是對應(yīng)上的。所以我們要開啟二級緩存兩個(gè)條件都要滿足。

??這樣的設(shè)置表示當(dāng)前的映射文件中的相關(guān)查詢操作都會(huì)觸發(fā)二級緩存,但如果某些個(gè)別方法我們不希望走二級緩存怎么辦呢?我們可以在標(biāo)簽中添加一個(gè) useCache=false 來實(shí)現(xiàn)的設(shè)置不使用二級緩存

還有就是當(dāng)我們執(zhí)行的對應(yīng)的DML操作,在MyBatis中會(huì)清空對應(yīng)的二級緩存和一級緩存。

private void flushCacheIfRequired(MappedStatement ms) {Cache cache = ms.getCache();// 增刪改查的標(biāo)簽上有屬性:flushCache="true" (select語句默認(rèn)是false)// 一級二級緩存都會(huì)被清理if (cache != null && ms.isFlushCacheRequired()) {tcm.clear(cache);}}

在解析映射文件的時(shí)候DML操作flushCacheRequired為true

4.4 第三方緩存

???在實(shí)際開發(fā)的時(shí)候我們一般也很少使用MyBatis自帶的二級緩存,這時(shí)我們會(huì)使用第三方的緩存工具Ehcache獲取Redis來實(shí)現(xiàn),那么他們是如何來實(shí)現(xiàn)的呢?

https://github.com/mybatis/redis-cache

添加依賴

<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-redis</artifactId><version>1.0.0-beta2</version> </dependency>

然后加上Cache標(biāo)簽的配置

<cache type="org.mybatis.caches.redis.RedisCache"eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

然后添加redis的屬性文件

host=192.168.100.120 port=6379 connectionTimeout=5000 soTimeout=5000 database=0

測試效果

數(shù)據(jù)存儲(chǔ)到了Redis中

??然后大家也可以自行分析下第三方的Cache是如何替換掉PerpetualCache的,因?yàn)镻erpetualCache是基于HashMap處理的,而RedisCache是基于Redis來存儲(chǔ)緩存數(shù)據(jù)的。

提示

緩存模塊我們就介紹到此。 然后大家可以基于我們上面所介紹的基礎(chǔ)支持層,再系統(tǒng)的來梳理下核心處理層的流程

~~ 好了,緩存模塊的內(nèi)容就給大家介紹到這里,如果對你有幫助,歡迎點(diǎn)贊關(guān)注加收藏
下篇我們介紹 MyBatis中的插件機(jī)制,敬請期待 V_V

總結(jié)

以上是生活随笔為你收集整理的带你彻底搞懂MyBatis的底层实现之缓存模块(Cache)-吊打面试官必备技能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。