<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>

          面試官:說(shuō)說(shuō) MyBatis 二級(jí)緩存?關(guān)聯(lián)刷新實(shí)現(xiàn)?我徹底懵B了。。

          共 6419字,需瀏覽 13分鐘

           ·

          2022-05-13 02:31

          關(guān)注我們,設(shè)為星標(biāo),每天7:40不見不散,架構(gòu)路上與您共享

          回復(fù)架構(gòu)師獲取資源


          大家好,我是你們的朋友架構(gòu)君,一個(gè)會(huì)寫代碼吟詩(shī)的架構(gòu)師。

          1、MyBatis緩存介紹

          Mybatis提供對(duì)緩存的支持,但是在沒(méi)有配置的默認(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。最新 MyBatis?面試題整理好了,點(diǎn)擊Java面試庫(kù)小程序在線刷題。

          注解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?{
          ????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

          "com.mars.system.dao.UserMapper">
          ????"com.mars.system.dao.UserMapper"/>
          ????"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}
          ????

          OrganizationMapper.java

          @Repository
          @CacheNamespace(implementation?=?RelativeCache.class,?eviction?=?RelativeCache.class,?flushInterval?=?30?*?60?*?1000)
          public?interface?OrganizationMapper?extends?BaseMapper?{
          }

          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ù)期。

          文章來(lái)源:http://blog.csdn.net/qq_38245668/article/details/105803298


          到此文章就結(jié)束了。Java架構(gòu)師必看一個(gè)集公眾號(hào)、小程序、網(wǎng)站(3合1的文章平臺(tái),給您架構(gòu)路上一臂之力,javajgs.com)。如果今天的文章對(duì)你在進(jìn)階架構(gòu)師的路上有新的啟發(fā)和進(jìn)步,歡迎轉(zhuǎn)發(fā)給更多人。歡迎加入架構(gòu)師社區(qū)技術(shù)交流群,眾多大咖帶你進(jìn)階架構(gòu)師,在后臺(tái)回復(fù)“加群”即可入群。



          這些年小編給你分享過(guò)的干貨


          1.idea永久激活碼(親測(cè)可用)

          2.優(yōu)質(zhì)ERP系統(tǒng)帶進(jìn)銷存財(cái)務(wù)生產(chǎn)功能(附源碼)

          3.優(yōu)質(zhì)SpringBoot帶工作流管理項(xiàng)目(附源碼)

          4.最好用的OA系統(tǒng),拿來(lái)即用(附源碼)

          5.SBoot+Vue外賣系統(tǒng)前后端都有(附源碼

          6.SBoot+Vue可視化大屏拖拽項(xiàng)目(附源碼)



          轉(zhuǎn)發(fā)在看就是最大的支持??

          瀏覽 16
          點(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>
                  国产AV黄片 | 东京热小视频 | 中国毛片网站 | 学生妹妹毛片 | 大香蕉综合一二 |