目录
- 前置
- pom: jar
- 配置文件: ehcache.xml
- 配置指定方式
- 恢复 .index 文件 (ApplicationClosedEventListener.java)
- 效果图
前置
会演示二级缓存生效/失效的场景
项目地址: https://gitee.com/xmaxm/test-code/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>
配置文件: 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>
配置指定方式
配置:
@CacheNamespace(implementation = EhcacheCache.class)
或 <cache type=“org.mybatis.caches.redis.EhcacheCache”/>
备注:
@CacheNamespace(implementation = EhcacheCache.class)
对应的xml文件:
<cache-ref namespace=“com.chaim.mybatis.cache.two.mappers.SysUserMapper”/>
---------------------------------------
@CacheNamespaceRef(value = SysUserMapper.class)
对应的xml文件:
<cache type=“org.mybatis.caches.redis.EhcacheCache”/>
@CacheNamespace(implementation = EhcacheCache.class)
public interface SysUserMapper extends BaseMapper<SysUser> {
}
<cache type="org.mybatis.caches.redis.EhcacheCache"/>
恢复 .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("程序已停止");
}
}
效果图