<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MyBatis 二級(jí)緩存 關(guān)聯(lián)刷新實(shí)現(xiàn)

          共 6504字,需瀏覽 14分鐘

           ·

          2022-02-12 12:57

          程序員的成長(zhǎng)之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
          關(guān)注


          閱讀本文大概需要 6?分鐘。

          來(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)證

          查詢SQL
          SELECT
          ?u.*,?o.name?org_name?
          FROM
          ?user?u
          ?LEFT?JOIN?organization?o?ON?u.org_id?=?o.id?
          WHERE
          ?u.id?=?#{userId}
          UserMapper
          UserInfo?queryUserInfo(@Param("userId")?String?userId);
          UserService
          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 Plus
          mybatis-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?CACHE_MAP?=?new?ConcurrentHashMap<>();

          ????private?List?relations?=?new?ArrayList<>();

          ????private?ReadWriteLock?readWriteLock?=?new?ReentrantReadWriteLock(true);

          ????private?String?id;
          ????private?Class?mapperClass;
          ????private?boolean?clearing;

          ????public?RelativeCache(String?id)?throws?Exception?{
          ????????this.id?=?id;
          ????????this.mapperClass?=?Class.forName(id);
          ????????RelativeCacheContext.putCache(mapperClass,?this);
          ????????loadRelations();
          ????}

          ????@Override
          ????public?String?getId()?{
          ????????return?id;
          ????}

          ????@Override
          ????public?void?putObject(Object?key,?Object?value)?{
          ????????CACHE_MAP.put(key,?value);
          ????}

          ????@Override
          ????public?Object?getObject(Object?key)?{
          ????????return?CACHE_MAP.get(key);
          ????}

          ????@Override
          ????public?Object?removeObject(Object?key)?{
          ????????return?CACHE_MAP.remove(key);
          ????}

          ????@Override
          ????public?void?clear()?{
          ????????ReadWriteLock?readWriteLock?=?getReadWriteLock();
          ????????Lock?lock?=?readWriteLock.writeLock();
          ????????lock.lock();
          ????????try?{
          ????????????//?判斷?當(dāng)前緩存是否正在清空,如果正在清空,取消本次操作
          ????????????//?避免緩存出現(xiàn)?循環(huán)?relation,造成遞歸無(wú)終止,調(diào)用棧溢出
          ????????????if?(clearing)?{
          ????????????????return;
          ????????????}
          ????????????clearing?=?true;
          ????????????try?{
          ????????????????CACHE_MAP.clear();
          ????????????????relations.forEach(RelativeCache::clear);
          ????????????}?finally?{
          ????????????????clearing?=?false;
          ????????????}
          ????????}?finally?{
          ????????????lock.unlock();
          ????????}


          ????}

          ????@Override
          ????public?int?getSize()?{
          ????????return?CACHE_MAP.size();
          ????}

          ????@Override
          ????public?ReadWriteLock?getReadWriteLock()?{
          ????????return?readWriteLock;
          ????}

          ????public?void?addRelation(RelativeCache?relation)?{
          ????????if?(relations.contains(relation)){
          ????????????return;
          ????????}
          ????????relations.add(relation);
          ????}

          ????void?loadRelations()?{
          ????????//?加載?其他緩存更新時(shí)?需要更新此緩存的?caches
          ????????//?將?此緩存?加入至這些?caches?的?relations?中
          ????????List?to?=?UN_LOAD_TO_RELATIVE_CACHES_MAP.get(mapperClass);
          ????????if?(to?!=?null)?{
          ????????????to.forEach(relativeCache?->?this.addRelation(relativeCache));
          ????????}
          ????????//?加載?此緩存更新時(shí)?需要更新的一些緩存?caches
          ????????//?將這些緩存?caches?加入?至?此緩存?relations?中
          ????????List?from?=?UN_LOAD_FROM_RELATIVE_CACHES_MAP.get(mapperClass);
          ????????if?(from?!=?null)?{
          ????????????from.forEach(relativeCache?->?relativeCache.addRelation(this));
          ????????}

          ????????CacheRelations?annotation?=?AnnotationUtils.findAnnotation(mapperClass,?CacheRelations.class);
          ????????if?(annotation?==?null)?{
          ????????????return;
          ????????}

          ????????Class[]?toMappers?=?annotation.to();
          ????????Class[]?fromMappers?=?annotation.from();

          ????????if?(toMappers?!=?null?&&?toMappers.length?>?0)?{
          ????????????for?(Class?c?:?toMappers)?{
          ????????????????RelativeCache?relativeCache?=?MAPPER_CACHE_MAP.get(c);
          ????????????????if?(relativeCache?!=?null)?{
          ????????????????????//?將找到的緩存添加到當(dāng)前緩存的relations中
          ????????????????????this.addRelation(relativeCache);
          ????????????????}?else?{
          ????????????????????//?如果找不到?to?cache,證明to?cache還未加載,這時(shí)需將對(duì)應(yīng)關(guān)系存放到?UN_LOAD_FROM_RELATIVE_CACHES_MAP
          ????????????????????//?也就是說(shuō)?c?對(duì)應(yīng)的?cache?需要?在?當(dāng)前緩存更新時(shí)?進(jìn)行更新
          ????????????????????List?relativeCaches?=?UN_LOAD_FROM_RELATIVE_CACHES_MAP.putIfAbsent(c,?new?ArrayList());
          ????????????????????relativeCaches.add(this);
          ????????????????}
          ????????????}
          ????????}

          ????????if?(fromMappers?!=?null?&&?fromMappers.length?>?0)?{
          ????????????for?(Class?c?:?fromMappers)?{
          ????????????????RelativeCache?relativeCache?=?MAPPER_CACHE_MAP.get(c);
          ????????????????if?(relativeCache?!=?null)?{
          ????????????????????//?將找到的緩存添加到當(dāng)前緩存的relations中
          ????????????????????relativeCache.addRelation(this);
          ????????????????}?else?{
          ????????????????????//?如果找不到?from?cache,證明from?cache還未加載,這時(shí)需將對(duì)應(yīng)關(guān)系存放到?UN_LOAD_TO_RELATIVE_CACHES_MAP
          ????????????????????//?也就是說(shuō)?c?對(duì)應(yīng)的?cache?更新時(shí)需要更新當(dāng)前緩存
          ????????????????????List?relativeCaches?=?UN_LOAD_TO_RELATIVE_CACHES_MAP.putIfAbsent(c,?new?ArrayList());
          ????????????????????relativeCaches.add(this);
          ????????????????}
          ????????????}
          ????????}
          ????}

          }
          緩存上下文RelativeCacheContext
          public?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);
          ????}

          }
          使用方式
          UserMapper.java
          @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)緩存化。
          UserMapper.xml
          <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>
          OrganizationMapper.java
          @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添加裝飾。
          至此,配置和編碼完成。
          開始驗(yàn)證:
          查詢 userId=1的用戶信息
          {
          ????"code":"1",
          ????"message":null,
          ????"data":{
          ????????"id":"1",
          ????????"username":"admin",
          ????????"password":"admin",
          ????????"orgName":"組織1"
          ????}
          }
          更新組織信息,將 組織1 改為 組織2
          {
          ????"code":"1",
          ????"message":null,
          ????"data":{
          ????????"id":"1",
          ????????"name":"組織2"
          ????}
          }
          再次查詢用戶信息
          {
          ????"code":"1",
          ????"message":null,
          ????"data":{
          ????????"id":"1",
          ????????"username":"admin",
          ????????"password":"admin",
          ????????"orgName":"組織2"
          ????}
          }
          符合預(yù)期。
          ????

          推薦閱讀:

          被罵慘的 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)取!? ? ? ? ? ? ? ??? ??? ? ? ? ? ? ? ? ? ?朕已閱?

          瀏覽 24
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜精品久久久久久久 | 欧美黄色免费 | 色欲影视,淫色淫香 | 欧美日韩小视频 | 精品人伦一区二区三区蜜桃免费 |