" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2023年01月16日 09:14:51   作者:OoZzzy  
本文主要介紹了MyBatis二級(jí)緩存實(shí)現(xiàn)關(guān)聯(lián)刷新,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

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

二級(jí)緩存是針對(duì)于應(yīng)用級(jí)別的緩存,也就是針對(duì)不同的SqlSession做到緩存。 當(dāng)開啟二級(jí)緩存時(shí),MyBatis會(huì)將首次查詢結(jié)果存入對(duì)于Mapper的全局緩存,如果中間不執(zhí)行該Mapper的數(shù)據(jù)更新操作,那么后續(xù)的相同查詢都將會(huì)從緩存中獲取。

2.二級(jí)緩存問題

根據(jù)二級(jí)緩存的介紹發(fā)現(xiàn),如果Mapper只是單表查詢,并不會(huì)出現(xià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ù)不一致問題驗(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"
 }
}

執(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"
 }
}

造成此問題原因?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 問題處理思路

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

@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<Object, Object> CACHE_MAP = new ConcurrentHashMap<>();

? ? private List<RelativeCache> 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,造成遞歸無終止,調(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<RelativeCache> to = UN_LOAD_TO_RELATIVE_CACHES_MAP.get(mapperClass);
? ? ? ? if (to != null) {
? ? ? ? ? ? to.forEach(relativeCache -> this.addRelation(relativeCache));
? ? ? ? }
? ? ? ? // 加載 此緩存更新時(shí) 需要更新的一些緩存 caches
? ? ? ? // 將這些緩存 caches 加入 至 此緩存 relations 中
? ? ? ? List<RelativeCache> 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
? ? ? ? ? ? ? ? ? ? // 也就是說 c 對(duì)應(yīng)的 cache 需要 在 當(dāng)前緩存更新時(shí) 進(jìn)行更新
? ? ? ? ? ? ? ? ? ? List<RelativeCache> relativeCaches = UN_LOAD_FROM_RELATIVE_CACHES_MAP.putIfAbsent(c, new ArrayList<RelativeCache>());
? ? ? ? ? ? ? ? ? ? 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
? ? ? ? ? ? ? ? ? ? // 也就是說 c 對(duì)應(yīng)的 cache 更新時(shí)需要更新當(dāng)前緩存
? ? ? ? ? ? ? ? ? ? List<RelativeCache> relativeCaches = UN_LOAD_TO_RELATIVE_CACHES_MAP.putIfAbsent(c, new ArrayList<RelativeCache>());
? ? ? ? ? ? ? ? ? ? relativeCaches.add(this);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }

}

緩存上下文RelativeCacheContext

public class RelativeCacheContext {

? ? // 存儲(chǔ)全量緩存的映射關(guān)系
? ? public static final Map<Class<?>, RelativeCache> MAPPER_CACHE_MAP = new ConcurrentHashMap<>();
? ? // 存儲(chǔ) Mapper 對(duì)應(yīng)緩存 需要to更新緩存,但是此時(shí) Mapper 對(duì)應(yīng)緩存還未加載
? ? // 也就是 Class<?> 對(duì)應(yīng)的緩存更新時(shí),需要更新 List<RelativeCache> 中的緩存
? ? public static final Map<Class<?>, List<RelativeCache>> UN_LOAD_TO_RELATIVE_CACHES_MAP = new ConcurrentHashMap<>();
? ? // 存儲(chǔ) Mapper 對(duì)應(yīng)緩存 需要from更新緩存,但是在 加載 Mapper 緩存時(shí),這些緩存還未加載
? ? // 也就是 List<RelativeCache> 中的緩存更新時(shí),需要更新 Class<?> 對(duì)應(yīng)的緩存
? ? public static final Map<Class<?>, List<RelativeCache>> 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:

@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)情況下是無效的,也就是說緩存并不會(huì)定時(shí)清理。ScheduledCache是對(duì)flushInterval 功能的實(shí)現(xiàn),MyBatis 的緩存體系是用裝飾器進(jìn)行功能擴(kuò)展的,所以,如果需要定時(shí)刷新,需要使用ScheduledCache給到 RelativeCache添加裝飾。

4.驗(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"
    }
}

到此這篇關(guān)于MyBatis二級(jí)緩存實(shí)現(xiàn)關(guān)聯(lián)刷新的文章就介紹到這了,更多相關(guān)MyBatis 關(guān)聯(lián)刷新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring中的@ControllerAdvice和ResponseBodyAdvice詳解

    Spring中的@ControllerAdvice和ResponseBodyAdvice詳解

    這篇文章主要介紹了Spring中的@ControllerAdvice和ResponseBodyAdvice詳解,@ControllerAdvice作用于@Controller修飾的類里面的所有方法,ResponseBodyAdvice作用于@ResponseBody注解修飾的方法,它可以對(duì)這些方法的返回值進(jìn)行修改,需要的朋友可以參考下
    2024-01-01
  • Java?如何獲取某年的第一天和最后一天

    Java?如何獲取某年的第一天和最后一天

    在統(tǒng)計(jì)的數(shù)據(jù)是時(shí)候,要統(tǒng)計(jì)某年的數(shù)據(jù),開始時(shí)間是某年的第一天,結(jié)束時(shí)間是某年的最后一天,該如何獲取某年的第一天和最后一天,今天通過本文介紹下Java獲取某年的第一天和最后一天,需要的朋友可以參考下
    2023-07-07
  • 史上最全Java8日期時(shí)間工具類(分享)

    史上最全Java8日期時(shí)間工具類(分享)

    這篇文章主要介紹了史上最全Java8日期時(shí)間工具類(分享),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java簡單實(shí)現(xiàn)約瑟夫環(huán)算法示例

    Java簡單實(shí)現(xiàn)約瑟夫環(huán)算法示例

    這篇文章主要介紹了Java簡單實(shí)現(xiàn)約瑟夫環(huán)算法,簡單描述了約瑟夫環(huán)問題,并結(jié)合實(shí)例形式分析了Java實(shí)現(xiàn)約瑟夫環(huán)的具體操作技巧,需要的朋友可以參考下
    2017-09-09
  • Java線性結(jié)構(gòu)中的雙向鏈表實(shí)現(xiàn)原理

    Java線性結(jié)構(gòu)中的雙向鏈表實(shí)現(xiàn)原理

    這篇文章將給大家詳細(xì)講解雙向鏈表的內(nèi)容,尤其是會(huì)通過代碼來進(jìn)行鏈表的操作,文中的代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-07-07
  • spring,mybatis事務(wù)管理配置與@Transactional注解使用詳解

    spring,mybatis事務(wù)管理配置與@Transactional注解使用詳解

    這篇文章主要介紹了spring,mybatis事務(wù)管理配置與@Transactional注解使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 單機(jī)redis分布式鎖實(shí)現(xiàn)原理解析

    單機(jī)redis分布式鎖實(shí)現(xiàn)原理解析

    這篇文章主要介紹了單機(jī)redis分布式鎖實(shí)現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Mybatis?List列表In查詢實(shí)現(xiàn)的注意事項(xiàng)說明

    Mybatis?List列表In查詢實(shí)現(xiàn)的注意事項(xiàng)說明

    這篇文章主要介紹了Mybatis?List列表In查詢實(shí)現(xiàn)的注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • IDEA編寫JavaWeb出現(xiàn)亂碼問題解決方案

    IDEA編寫JavaWeb出現(xiàn)亂碼問題解決方案

    這篇文章主要介紹了IDEA編寫JavaWeb出現(xiàn)亂碼問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 解決Springboot-application.properties中文亂碼問題

    解決Springboot-application.properties中文亂碼問題

    這篇文章主要介紹了解決Springboot-application.properties中文亂碼問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評(píng)論

波密县| 乌兰浩特市| 太仆寺旗| 威信县| 耒阳市| 乾安县| 乌审旗| 汨罗市| 河北省| 天峻县| 樟树市| 金沙县| 信丰县| 宜阳县| 双峰县| 东兴市| 阜新市| 武城县| 海林市| 方城县| 进贤县| 乌恰县| 仁寿县| 宜兰市| 栖霞市| 柘荣县| 凤阳县| 老河口市| 洞头县| 宜章县| 文水县| 凯里市| 固阳县| 修武县| 涞水县| 揭阳市| 延寿县| 新田县| 泰兴市| 鄱阳县| 永州市|