• Mybatis的二级缓存 (ehcache方式)



    前置

    会演示二级缓存生效/失效的场景
    项目地址: https://gitee.com/xmaxm/chaim-code-template/blob/master/chaim-cache/chaim-mybatis-cache/chaim-mybatis-cache-two/README.md

    前置配置:

    本篇文章是基于上篇文章进行: https://blog.csdn.net/qq_38637558/article/details/127924334

    注意点

    官网地址:
    https://www.ehcache.org/documentation
    http://mybatis.org/ehcache-cache/index.html
    如清除策略、可读或可读写等,不能应用于自定义缓存

    强调:

    通过.index文件进行数据的恢复. 可参考 ApplicationClosedEventListener.java
    磁盘已经写入了数据, 重启项目的时候, 发现还是从数据库查, 第一次命中率还是0, 需要执行以下方法.

    源码部分

    感觉要是把源码过一遍, 得从新起一篇文章才行, 后面有需要在写, 偷个懒吧, 哈哈哈哈哈!

    源码入口: org.apache.ibatis.mapping.CacheBuilder#build
    关键类: org.apache.ibatis.cache.Cache
    实现类: org.mybatis.caches.ehcache.EhcacheCache
    org.mybatis.caches.ehcache.AbstractEhcacheCache
    淘汰策略: 没得

    相关缓存文章

    Mybatis的一级缓存
    Mybatis的二级缓存 (默认方式)
    Mybatis的二级缓存 (Redis方式)
    Mybatis的二级缓存 (ehcache方式)


    pom: jar

    没有使用过这个cache框架, 不过整体实现代码也没有几个类, 可以读一下

    <!-- 自定义二级缓存存储: ehcache方式 -->
    <dependency>
    	<groupId>org.mybatis.caches</groupId>
    	<artifactId>mybatis-ehcache</artifactId>
    	<version>1.2.2</version>
    	<!-- 不传递依赖 -->
    	<optional>true</optional>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    配置文件: ehcache.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
             updateCheck="false">
    
        <!-- 磁盘缓存位置
        path属性可以配置的目录有:
        user.home(用户的家目录)
        user.dir(用户当前的工作目录)
        java.io.tmpdir(默认的临时目录)
        ehcache.disk.store.dir(ehcache的配置目录)
        绝对路径(如:d:\\ehcache)
        -->
        <diskStore path="F:\upload\EhCache"/>
    
        <!--
            name:缓存名称。
            maxElementsInMemory:缓存最大个数。
            eternal:对象是否永久有效,一但设置了,timeout将不起作用。
            timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
            timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
            overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
            diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
            maxElementsOnDisk:硬盘最大缓存个数。
            diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
            diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
            memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
            clearOnFlush:内存数量最大时是否清除。
        -->
    
    
        <!-- 默认缓存 -->
        <defaultCache
                eternal="true"
                maxElementsInMemory="1000"
                overflowToDisk="false"
                diskPersistent="true"
                timeToIdleSeconds="0"
                timeToLiveSeconds="0"
                memoryStoreEvictionPolicy="LRU"/>
    
        <!-- 自定义缓存 -->
        <cache
                name="MyCache"
                eternal="true"
                maxElementsInMemory="1000"
                overflowToDisk="false"
                diskPersistent="true"
                timeToIdleSeconds="0"
                timeToLiveSeconds="0"
                memoryStoreEvictionPolicy="LRU">
    
            <!-- 初始化缓存,以及自动设置. 重启项目取到缓存数据 -->
            <!-- 通过ApplicationClosedEventListener.java配置文件生效, 该配置并未生效 -->
            <!--<BootstrapCacheLoaderFactory-->
            <!--        class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"-->
            <!--        properties="bootstrapAsynchronously=true" />-->
        </cache>
    
    
    </ehcache>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    配置指定方式

    配置:
    @CacheNamespace(implementation = EhcacheCache.class)

    备注:
    @CacheNamespace(implementation = EhcacheCache.class)
    对应的xml文件:

    ---------------------------------------
    @CacheNamespaceRef(value = SysUserMapper.class)
    对应的xml文件:

    @CacheNamespace(implementation = EhcacheCache.class)
    public interface SysUserMapper extends BaseMapper<SysUser> {
    }
    
    • 1
    • 2
    • 3
    <cache type="org.mybatis.caches.redis.EhcacheCache"/>
    
    • 1

    恢复 .index 文件 (ApplicationClosedEventListener.java)

    package com.chaim.mybatis.cache.two.config;
    
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 监听项目停止运行
     * 用于处理 mybatis-ehcache 停止时调用 shutdown() 方法, 生成用于恢复的 .index 文件
     * 当 CacheManager 存在的时候该类才会进行 IOC
     *
     * @author Chaim
     * @date 2022/9/10 2:14
     */
    @ConditionalOnClass(name = {"net.sf.ehcache.CacheManager"})
    @Component
    @Slf4j
    public class ApplicationClosedEventListener implements ApplicationListener<ContextClosedEvent> {
    
        /**
         * 这一串最终执行的代码:
         * CacheManager cacheManager = CacheManager.getInstance();
         * cacheManager.shutdown();
         * 考虑到可能自定义二级缓存不采用此方式, 那么pom就不会导入对应的jar, 避免代码报错使用反射方式
         *
         * @param contextClosedEvent
         */
        @SneakyThrows
        @Override
        public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
            // 通过反射避免引入包 CacheManager 不存在编译报错
            Class<?> aClass = Class.forName("net.sf.ehcache.CacheManager");
    
            // 静态方法
            Method getInstance = aClass.getMethod("getInstance");
            Object invoke = getInstance.invoke(aClass);
    
            Method shutdown = aClass.getMethod("shutdown");
            shutdown.invoke(invoke);
            log.info("程序已停止");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    效果图

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    作业 day6
    1 Getting Started with Database Administration
    路径规划之规划节点和连接节点中规划节点的序号代码
    10、MySQL-索引
    24.bytebuf创建
    数据结构与算法4-链表
    设置服务账号Service Accounts(sa)的token不挂载到pod
    【操作系统】安全管理/防火墙
    源码级深度理解 Java SPI
    聚氨基甲酸酯发泡材料检测范围及检测方法
  • 原文地址:https://blog.csdn.net/qq_38637558/article/details/127944619