互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
來(lái)自:blog.csdn.net/qq_38245668/article/details/105803298
1、MyBatis緩存介紹
Mybatis提供對(duì)緩存的支持,但是在沒有配置的默認(rèn)情況下,它只開啟一級(jí)緩存,二級(jí)緩存需要手動(dòng)開啟。一級(jí)緩存只是相對(duì)于同一個(gè)SqlSession而言。?也就是針對(duì)于同一事務(wù),多次執(zhí)行同一Mapper的相同查詢方法,第一查詢后,MyBatis會(huì)將查詢結(jié)果放入緩存,在中間不涉及相應(yīng)Mapper的數(shù)據(jù)更新(Insert,Update和Delete)操作的情況下,后續(xù)的查詢將會(huì)從緩存中獲取,而不會(huì)查詢數(shù)據(jù)庫(kù)。二級(jí)緩存是針對(duì)于應(yīng)用級(jí)別的緩存,也就是針對(duì)不同的SqlSession做到緩存。?當(dāng)開啟二級(jí)緩存時(shí),MyBatis會(huì)將首次查詢結(jié)果存入對(duì)于Mapper的全局緩存,如果中間不執(zhí)行該Mapper的數(shù)據(jù)更新操作,那么后續(xù)的相同查詢都將會(huì)從緩存中獲取。2、二級(jí)緩存問(wèn)題
根據(jù)二級(jí)緩存的介紹發(fā)現(xiàn),如果Mapper只是單表查詢,并不會(huì)出現(xiàn)問(wèn)題,但是如果Mapper涉及的查詢出現(xiàn) 聯(lián)表 查詢,如 UserMapper 在查詢 user 信息時(shí)需要關(guān)聯(lián)查詢 組織信息,也就是需要 user 表和 organization 表關(guān)聯(lián),OrganizationMapper 在執(zhí)行更新時(shí)并不會(huì)更新 UserMapper 的緩存,結(jié)果會(huì)導(dǎo)致在使用相同條件 使用 UserMapper 查詢 user 信息時(shí),會(huì)等到未更新前的 organization 信息,造成數(shù)據(jù)不一致的情況。2.1、數(shù)據(jù)不一致問(wèn)題驗(yàn)證
SELECT
?u.*,?o.name?org_name?
FROM
?user?u
?LEFT?JOIN?organization?o?ON?u.org_id?=?o.id?
WHERE
?u.id?=?#{userId}
UserInfo?queryUserInfo(@Param("userId")?String?userId);
public?UserEntity?queryUser(String?userId)?{
????UserInfo?userInfo?=?userMapper.queryUserInfo(userId);
????return?userInfo;
}
調(diào)用查詢,得到查詢結(jié)果(多次查詢,得到緩存數(shù)據(jù)),這里?userId = 1,data為user查詢結(jié)果{
?"code":?"1",
?"message":?null,
?"data":?{
???"id":?"1",
???"username":?"admin",
???"password":?"admin",
???"orgName":?"組織1"
?}
}
查詢 對(duì)應(yīng) organization 信息,結(jié)果{
?"code":?"1",
?"message":?null,
?"data":?{
???"id":?"1",
???"name":?"組織1"
?}
}
發(fā)現(xiàn)和user緩存數(shù)據(jù)一致。執(zhí)行更新 organization 操作,將 組織1 改為 組織2,再次查詢組織信息{
?"code":?"1",
?"message":?null,
?"data":?{
???"id":?"1",
???"name":?"組織2"
?}
}
再次查詢user信息,發(fā)現(xiàn)依舊從緩存中獲取{
?"code":?"1",
?"message":?null,
?"data":?{
???"id":?"1",
???"username":?"admin",
???"password":?"admin",
???"orgName":?"組織1"
?}
}
造成此問(wèn)題原因?yàn)?organization 數(shù)據(jù)信息更新只會(huì)自己Mapper對(duì)應(yīng)的緩存數(shù)據(jù),而不會(huì)通知到關(guān)聯(lián)表organization 的一些Mapper更新對(duì)應(yīng)的緩存數(shù)據(jù)。2.2、問(wèn)題處理思路
- 在 Mapper1 定義時(shí),手動(dòng)配置 相應(yīng)的關(guān)聯(lián) Mapper2
- 在 Mapper1 緩存 cache1 實(shí)例化時(shí),讀取 所關(guān)聯(lián)的 Mapper2 的緩存 cache2相關(guān)信息
- 在 cache1 中存儲(chǔ) cache2 的引用信息
- cache1 執(zhí)行clear時(shí),同步操作 cache2 執(zhí)行clear
3、關(guān)聯(lián)緩存刷新實(shí)現(xiàn)
打開二級(jí)緩存,本地項(xiàng)目使用 MyBatis Plusmybatis-plus.configuration.cache-enabled=true主要用到自定義注解CacheRelations,自定義緩存實(shí)現(xiàn)RelativeCache和緩存上下文RelativeCacheContext。注解CacheRelations,使用時(shí)需標(biāo)注在對(duì)應(yīng)mapper上@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public?@interface?CacheRelations?{
????//?from中mapper?class對(duì)應(yīng)的緩存更新時(shí),需要更新當(dāng)前注解標(biāo)注mapper的緩存
????Class>[]?from()?default?{};
????//?當(dāng)前注解標(biāo)注mapper的緩存更新時(shí),需要更新to中mapper?class對(duì)應(yīng)的緩存
????Class>[]?to()?default?{};
}
自定義緩存RelativeCache實(shí)現(xiàn) MyBatis Cache 接口public?class?RelativeCache?implements?Cache?{
????private?Map
緩存上下文RelativeCacheContextpublic?class?RelativeCacheContext?{
????//?存儲(chǔ)全量緩存的映射關(guān)系
????public?static?final?Map,?RelativeCache>?MAPPER_CACHE_MAP?=?new?ConcurrentHashMap<>();
????//?存儲(chǔ)?Mapper?對(duì)應(yīng)緩存?需要to更新緩存,但是此時(shí)?Mapper?對(duì)應(yīng)緩存還未加載
????//?也就是?Class>?對(duì)應(yīng)的緩存更新時(shí),需要更新?List?中的緩存
????public?static?final?Map,?List>?UN_LOAD_TO_RELATIVE_CACHES_MAP?=?new?ConcurrentHashMap<>();
????//?存儲(chǔ)?Mapper?對(duì)應(yīng)緩存?需要from更新緩存,但是在?加載?Mapper?緩存時(shí),這些緩存還未加載
????//?也就是?List?中的緩存更新時(shí),需要更新?Class>?對(duì)應(yīng)的緩存
????public?static?final?Map,?List>?UN_LOAD_FROM_RELATIVE_CACHES_MAP?=?new?ConcurrentHashMap<>();
????public?static?void?putCache(Class>?clazz,?RelativeCache?cache)?{
????????MAPPER_CACHE_MAP.put(clazz,?cache);
????}
????public?static?void?getCache(Class>?clazz)?{
????????MAPPER_CACHE_MAP.get(clazz);
????}
}
@Repository
@CacheNamespace(implementation?=?RelativeCache.class,?eviction?=?RelativeCache.class,?flushInterval?=?30?*?60?*?1000)
@CacheRelations(from?=?OrganizationMapper.class)
public?interface?UserMapper?extends?BaseMapper<UserEntity>?{
????UserInfo?queryUserInfo(@Param("userId")?String?userId);
}
queryUserInfo是xml實(shí)現(xiàn)的接口,所以需要在對(duì)應(yīng)xml中配置,不然查詢結(jié)果不會(huì)被緩存化。如果接口為 BaseMapper實(shí)現(xiàn),查詢結(jié)果會(huì)自動(dòng)緩存化。<mapper?namespace="com.mars.system.dao.UserMapper">
????<cache-ref?namespace="com.mars.system.dao.UserMapper"/>
????<select?id="queryUserInfo"?resultType="com.mars.system.model.UserInfo">
????????select?u.*,?o.name?org_name?from?user?u?left?join?organization?o?on?u.org_id?=?o.id
????????where?u.id?=?#{userId}
????select>
mapper>
@Repository
@CacheNamespace(implementation?=?RelativeCache.class,?eviction?=?RelativeCache.class,?flushInterval?=?30?*?60?*?1000)
public?interface?OrganizationMapper?extends?BaseMapper<OrganizationEntity>?{
}
CacheNamespace中flushInterval 在默認(rèn)情況下是無(wú)效的,也就是說(shuō)緩存并不會(huì)定時(shí)清理。ScheduledCache是對(duì)flushInterval 功能的實(shí)現(xiàn),MyBatis 的緩存體系是用裝飾器進(jìn)行功能擴(kuò)展的,所以,如果需要定時(shí)刷新,需要使用ScheduledCache給到 RelativeCache添加裝飾。{
????"code":"1",
????"message":null,
????"data":{
????????"id":"1",
????????"username":"admin",
????????"password":"admin",
????????"orgName":"組織1"
????}
}
{
????"code":"1",
????"message":null,
????"data":{
????????"id":"1",
????????"name":"組織2"
????}
}
{
????"code":"1",
????"message":null,
????"data":{
????????"id":"1",
????????"username":"admin",
????????"password":"admin",
????????"orgName":"組織2"
????}
}
推薦閱讀:
被罵慘的 Windows 11 還是“真香”了:下月將支持 Android 應(yīng)用,產(chǎn)品滿意度歷代最高!
扎克伯格“氣哭”了:Meta搞元宇宙巨虧,股價(jià)暴跌市值蒸發(fā)2000多億美元
互聯(lián)網(wǎng)初中高級(jí)大廠面試題(9個(gè)G)內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬(wàn)并發(fā)、消息隊(duì)列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper、數(shù)據(jù)結(jié)構(gòu)、限流熔斷降級(jí)......等技術(shù)棧!
?戳閱讀原文領(lǐng)取!? ? ? ? ? ? ? ??? ??? ? ? ? ? ? ? ? ? ?朕已閱?