Mybatis缓存机制

it2022-05-05  93

 

概述:MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能 。

 

一、mybatis缓存机制的整体设计以及二级缓存的工作模式

如上图所示,当开启一个会话时,一个SqlSession对象会使用一个Executor对象来完成会话操作,MyBatis的二级缓存机制的关键就是对这个Executor对象做文章。如果用户配置了"cacheEnabled=true",那么MyBatis在为SqlSession对象创建Executor对象时,会对Executor对象加上一个装饰者:CachingExecutor,这时SqlSession使用CachingExecutor对象来完成操作请求。CachingExecutor对于查询请求,会先判断该查询请求在Application级别的二级缓存中是否有缓存结果,如果有查询结果,则直接返回缓存结果;如果缓存中没有,再交给真正的Executor对象来完成查询操作,之后CachingExecutor会将真正Executor返回的查询结果放置到缓存中,然后在返回给用户。

注释:Executor是一个比Windows“运行”功能更为强大、自定义更强的工具,如果你需要经常查找程序或者想快速运行程序这个功能比较适合你。功能大致有:自定义关键字、历史、自动完成、下拉菜单,Executor会自动匹配关键字,并提供下拉列表选项 。

 

CachingExecutor是Executor的装饰者,以增强Executor的功能,使其具有缓存查询的功能,这里用到了设计模式中的装饰者模式。

二、Mybatis二级缓存划分

MyBatis并不是简单地对整个Application就只有一个Cache缓存对象,它将缓存划分的更细,即是Mapper级别的,即每一个Mapper都可以拥有一个Cache对象具体如下:

  **a.为每一个Mapper分配一个Cache缓存对象(使用<cache>节点配置);**   

**b.多个Mapper共用一个Cache缓存对象(使用<cache-ref>节点配置);**

多个Mapper共用一个Cache缓存对象(使用节点配置)

如果你想让多个Mapper公用一个Cache的话,你可以使用<cache-ref namespace="">节点,来指定你的这个Mapper使用到了哪一个Mapper的Cache缓存。

 

三、使用二级缓存必须要具备的条件

MyBatis对二级缓存的支持粒度很细,它会指定某一条查询语句是否使用二级缓存。

虽然在Mapper中配置了<cache>,并且为此Mapper分配了Cache对象,这并不表示我们使用Mapper中定义的查询语句查到的结果都会放置到Cache对象之中,我们必须指定Mapper中的某条选择语句是否支持缓存,即如下所示,在<select> 节点中配置useCache="true",Mapper才会对此Select的查询支持缓存特性,否则,不会对此Select查询,不会经过Cache缓存。如下所示,Select语句配置了useCache="true",则表明这条Select语句的查询会使用二级缓存。<select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" useCache="true">

总之,要想使某条Select查询支持二级缓存,你需要保证:

MyBatis支持二级缓存的总开关:全局配置变量参数   cacheEnabled=true

该select语句所在的Mapper,配置了<cache> 或<cached-ref>节点,并且有效

该select语句的参数 useCache=true

 

四、一级缓存和 二级缓存的使用顺序

请注意,如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:

 二级缓存 ———> 一级缓存——> 数据库

五、二级缓存实现的选择

MyBatis对二级缓存的设计非常灵活,它自己内部实现了一系列的Cache缓存实现类,并提供了各种缓存刷新策略如LRU,FIFO等等;另外,MyBatis还允许用户自定义Cache接口实现,用户是需要实现org.apache.ibatis.cache.Cache接口,然后将Cache实现类配置在<cache  type="">节点的type属性上即可;除此之外,MyBatis还支持跟第三方内存缓存库如Memecached的集成,总之,使用MyBatis的二级缓存有三个选择:

 

1.MyBatis自身提供的缓存实现;

用户自定义的Cache接口实现;

跟第三方内存缓存库的集成;

5.1、Mybatis自身提供的二级缓存的实现

MyBatis自身提供了丰富的,并且功能强大的二级缓存的实现,它拥有一系列的Cache接口装饰者,可以满足各种对缓存操作和更新的策略。

MyBatis定义了大量的Cache的装饰器来增强Cache缓存的功能,如下类图所示。

对于每个Cache而言,都有一个容量限制,MyBatis各供了各种策略来对Cache缓存的容量进行控制,以及对Cache中的数据进行刷新和置换。下面来看不同置换策略实现类的源码:

5.1.1Cache接口以及实现

Cache接口

MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口。

 

public interface Cache {     String getId(); void putObject(Object key, Object value); Object getObject(Object key); Object removeObject(Object key); void clear(); int getSize(); ReadWriteLock getReadWriteLock(); }

C

Cache实现

Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现。

perpetualCache

作为为最基础的缓存类,底层实现比较简单,直接使用了HashMap。

FifoCache

FIFO回收策略,装饰类,内部维护了一个队列,来保证FIFO,一旦超出指定的大小,则从队列中获取Key并从被包装的Cache中移除该键值对。

public class FifoCache implements Cache { ​ // 被包装的类 private final Cache delegate; // 队列,用来维持FIFO private LinkedList<Object> keyList; // 最大可容纳的大小 private int size; ​ public FifoCache(Cache delegate) {   this.delegate = delegate;   this.keyList = new LinkedList<Object>();   this.size = 1024; } ​ @Override public void putObject(Object key, Object value) {   // 将Key放入队列中,并且检查一遍,如果满了则移除队列头部的元素   cycleKeyList(key);   // 执行真正的操作   delegate.putObject(key, value); } ​ private void cycleKeyList(Object key) {   // 将Key放入队列   keyList.addLast(key);   if (keyList.size() > size) {     // 超出指定容量,移除队列头部Key     Object oldestKey = keyList.removeFirst();     // 从缓存中移除Key对应的值     delegate.removeObject(oldestKey);   } } ​ // 省略部分代码 ... }

LogginCache

日志功能,装饰类,用于记录缓存的命中率,如果开启了DEBUG模式,则会输出命中率日志。

public class LoggingCache implements Cache {   private Log log;     private Cache delegate;   // 请求次数   protected int requests = 0;   // 命中次数   protected int hits = 0;   public LoggingCache(Cache delegate) {     this.delegate = delegate;     this.log = LogFactory.getLog(getId());   }   @Override   public Object getObject(Object key) {     requests++;     final Object value = delegate.getObject(key);     if (value != null) {       // 命中       hits++;     }     if (log.isDebugEnabled()) {       // 如果开启了DEBUG模式,则输出命中率       log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());     }     return value;   }   // 获取命中率   private double getHitRatio() {     return (double) hits / (double) requests;   }   // 省略部分代码   ...

LRUCache

LRU回收策略,装饰类,在内部保存一个LinkedHashMap,用以实现LRU 。

public class LruCache implements Cache { ​ private final Cache delegate; private Map<Object, Object> keyMap; private Object eldestKey; ​ public LruCache(Cache delegate) {   this.delegate = delegate;   // 初始化设置LRU回收的边界容量   setSize(1024); } ​ public void setSize(final int size) {   keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {     private static final long serialVersionUID = 4267176411845948333L; ​     // 键值移除策略,当大于指定容量时则移除最近最少使用的key/value     protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {       boolean tooBig = size() > size;       if (tooBig) {         // 保存需要移除的键,因为在被包装的类中并不知道什么键需要移除         eldestKey = eldest.getKey();       }       return tooBig;     }   }; } ​ @Override public void putObject(Object key, Object value) {   delegate.putObject(key, value);   // 将当前Key放到LRU的Map中,如果大于指定容量,则移除筛选的键值对   cycleKeyList(key); } ​ @Override public Object getObject(Object key) {   // 让当前LRU的Map知道使用过   keyMap.get(key); //touch   return delegate.getObject(key); } ​ @Override public Object removeObject(Object key) {   // 这里没有移除当前维护的key,不过在后续也会被回收,可以忽略   return delegate.removeObject(key); } ​ private void cycleKeyList(Object key) {   keyMap.put(key, key);   if (eldestKey != null) {     // 从Cache中移除掉LRU筛选出的键值对     delegate.removeObject(eldestKey);     eldestKey = null;   } } // 省略部分代码... }

ScheduledCache

定时清空Cache,但是并没有开始一个定时任务,而是在使用Cache的时候,才去检查时间是否到了。

public class ScheduledCache implements Cache { ​ private Cache delegate; // 清除的时间间隔 protected long clearInterval; // 上一次清除的时间 protected long lastClear; ​ public ScheduledCache(Cache delegate) {   this.delegate = delegate;   this.clearInterval = 60 * 60 * 1000; // 1 hour   this.lastClear = System.currentTimeMillis(); } ​ public void setClearInterval(long clearInterval) {   this.clearInterval = clearInterval; } ​ @Override public Object getObject(Object key) {   if (clearWhenStale()) {     return null;   } else {     return delegate.getObject(key);   } } ​ private boolean clearWhenStale() {   if (System.currentTimeMillis() - lastClear > clearInterval) {     // 时间到了,清空     clear();     return true;   }   return false; } ​ @Override public void clear() {   // 更新清空时间   lastClear = System.currentTimeMillis();   delegate.clear(); } // 省略部分代码 }

SynchronizedCache

同步Cache,实现比较简单,直接使用synchronized修饰方法。

public class SynchronizedCache implements Cache { ​ private Cache delegate; ​ public SynchronizedCache(Cache delegate) {   this.delegate = delegate; } @Override public synchronized void putObject(Object key, Object object) {   delegate.putObject(key, object); } // 省略部分代码 }

SoftCache

软引用回收策略,软引用只有当内存不足时才会被垃圾收集器回收。这里的实现机制中,使用了一个链表来保证一定数量的值即使内存不足也不会被回收,但是没有保存在该链表的值则有可能会被回收。 在WeakHashMap中,可以看到是将引用应用到Key的,当Key被回收后,则移除相关的Value。但是这里是将其应用到Value中,因为Key不能被回收,如果被移除的话,就会影响到整个体系,最底层的实现使用HashMap实现的,没有Key,就没有办法移除相关的值。反过来,值被回收了,将软引用对象放到队列中,可以根据Key调用removeObject移除该关联的键和软引用对象 。

public class SoftCache implements Cache { // 用于保存一定数量强引用的值 private final LinkedList<Object> hardLinksToAvoidGarbageCollection; // 引用队列,当被垃圾收集器回收时,会将软引用对象放入此队列 private final ReferenceQueue<Object> queueOfGarbageCollectedEntries; private final Cache delegate; // 保存强引用值的数量 private int numberOfHardLinks; ​ public SoftCache(Cache delegate) {   this.delegate = delegate;   this.numberOfHardLinks = 256;   this.hardLinksToAvoidGarbageCollection = new LinkedList<Object>();   this.queueOfGarbageCollectedEntries = new ReferenceQueue<Object>(); } ​ @Override public void putObject(Object key, Object value) {   // 移除被垃圾收集器回收的键值   removeGarbageCollectedItems();   // 将软件用作用到Value中   delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries)); } ​ @Override public Object getObject(Object key) {   Object result = null;   @SuppressWarnings("unchecked")   SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);   if (softReference != null) {     result = softReference.get();     if (result == null) {       // 该值被垃圾收集器回收,移除掉该项       delegate.removeObject(key);     } else {       // 这里以及下面的clear,想不通为什么要加hardLinksToAvoidGarbageCollection的同步?(在WeakCache中却没有加同步)       synchronized (hardLinksToAvoidGarbageCollection) {         hardLinksToAvoidGarbageCollection.addFirst(result);         if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {           // 超出容量,则移除最先保存的引用           hardLinksToAvoidGarbageCollection.removeLast();         }       }     }   }   return result; } ​ @Override public Object removeObject(Object key) {   // 移除被垃圾收集器回收的键值   removeGarbageCollectedItems();   return delegate.removeObject(key); } ​ @Override public void clear() {   synchronized (hardLinksToAvoidGarbageCollection) {     // 这里需要清空该队列,否则即使下面调用clear,其Map清空了,但是部分值保留有引用,垃圾收集器也不会回收,会造成短暂的内存泄漏。     hardLinksToAvoidGarbageCollection.clear();   }   removeGarbageCollectedItems();   delegate.clear(); } ​ private void removeGarbageCollectedItems() {   SoftEntry sv;   // 清空被垃圾收集器回收的value其相关联的键以及软引用   while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {     delegate.removeObject(sv.key);   } } ​ // 这里软引用对象是用在value中的 private static class SoftEntry extends SoftReference<Object> {   // 保存与value相关联的Key,因为一旦被垃圾收集器回收,则此软引用对象会被放到关联的引用队列中,这样就可以根据Key,移除该键值对。   private final Object key; ​   private SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {     super(value, garbageCollectionQueue);     this.key = key;   } } // 省略部分代码 }

WeakCache

弱引用回收策略,弱引用的对象一旦被垃圾收集器发现,则会被回收,无论内存是否足够。这里的实现和上面的软引用类似,除了使用WeakReference替换掉SoftReference,其它基本一样。

public Object getObject(Object key) {   Object result = null;   @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache   WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);   if (weakReference != null) {     result = weakReference.get();     if (result == null) {       delegate.removeObject(key);     } else {       // 软引用这里加锁了       hardLinksToAvoidGarbageCollection.addFirst(result);       if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {         hardLinksToAvoidGarbageCollection.removeLast();       }     }   }   return result; }

TransactionalCache

事务缓存,在提交的时候,才真正的放到Cache中,或者回滚的时候清除掉,对Cache没有影响。

public class TransactionalCache implements Cache { ​ private Cache delegate; private boolean clearOnCommit; private Map<Object, AddEntry> entriesToAddOnCommit; private Map<Object, RemoveEntry> entriesToRemoveOnCommit; ​ public TransactionalCache(Cache delegate) {   this.delegate = delegate;   this.clearOnCommit = false;   this.entriesToAddOnCommit = new HashMap<Object, AddEntry>();   this.entriesToRemoveOnCommit = new HashMap<Object, RemoveEntry>(); } ​ @Override public Object getObject(Object key) {   if (clearOnCommit) return null; // issue #146   return delegate.getObject(key); } ​ @Override public void putObject(Object key, Object object) {   // 移除当前事务中 待移除键值对操作   entriesToRemoveOnCommit.remove(key);   // 添加当前事务中 待增加到缓存键值对操作   entriesToAddOnCommit.put(key, new AddEntry(delegate, key, object)); } ​ @Override public Object removeObject(Object key) {   // 移除增加该键值对的操作   entriesToAddOnCommit.remove(key);   // 添加移除键值对操作   entriesToRemoveOnCommit.put(key, new RemoveEntry(delegate, key));   return delegate.getObject(key); } ​ @Override public void clear() {   reset();   clearOnCommit = true; } ​ public void commit() {   if (clearOnCommit) {     // 当提交事务时需要先清空,则清空缓存     delegate.clear();   } else {     // 应用移除键值对操作     for (RemoveEntry entry : entriesToRemoveOnCommit.values()) {       entry.commit();     }   }   // 应用添加键值对操作   for (AddEntry entry : entriesToAddOnCommit.values()) {     entry.commit();   }   reset(); } ​ public void rollback() {   reset(); } ​ private void reset() {   clearOnCommit = false;   entriesToRemoveOnCommit.clear();   entriesToAddOnCommit.clear(); } ​ private static class AddEntry {   private Cache cache;   private Object key;   private Object value; ​   public AddEntry(Cache cache, Object key, Object value) {     this.cache = cache;     this.key = key;     this.value = value;   } ​   public void commit() {     // 提交的时候,才真正放入缓存     cache.putObject(key, value);   } } ​ private static class RemoveEntry {   private Cache cache;   private Object key; ​   public RemoveEntry(Cache cache, Object key) {     this.cache = cache;     this.key = key;   } ​   public void commit() {     // 提交的时候才真正从缓存中移除     cache.removeObject(key);   } } }

SerializedCache

序列化功能,将值序列化后存到缓存中。该功能用于缓存返回一份实例的Copy,用于保存线程安全。

public class SerializedCache implements Cache { ​ // 省略部分代码 @Override public void putObject(Object key, Object object) {   if (object == null || object instanceof Serializable) {     // 先序列化后再存放到缓存中     delegate.putObject(key, serialize((Serializable) object));   } else {     throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);   } } ​ @Override public Object getObject(Object key) {   Object object = delegate.getObject(key);   // 不为空,则反序列化,生成一份Copy   return object == null ? null : deserialize((byte[]) object); } ​ private byte[] serialize(Serializable value) {   try {     // 序列化     ByteArrayOutputStream bos = new ByteArrayOutputStream();     ObjectOutputStream oos = new ObjectOutputStream(bos);     oos.writeObject(value);     oos.flush();     oos.close();     return bos.toByteArray();   } catch (Exception e) {     throw new CacheException("Error serializing object. Cause: " + e, e);   } } ​ private Serializable deserialize(byte[] value) {   Serializable result;   try {     // 反序列化     ByteArrayInputStream bis = new ByteArrayInputStream(value);     ObjectInputStream ois = new CustomObjectInputStream(bis);     result = (Serializable) ois.readObject();     ois.close();   } catch (Exception e) {     throw new CacheException("Error deserializing object. Cause: " + e, e);   }   return result; } ​ public static class CustomObjectInputStream extends ObjectInputStream { ​   public CustomObjectInputStream(InputStream in) throws IOException {     super(in);   } ​   @Override   protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {     // 此方法只有在待序列化的类第一次序列化的时候才会被调用     // 遍历所支持的ClassLoader,加载对应的Class     return Resources.classForName(desc.getName());   } } }

 

5.1.2 用户自定义的Cache接口实现

自定义缓存对象,该对象必须实现 org.apache.ibatis.cache.Cache 接口,如下:

 

import org.apache.ibatis.cache.Cache; ​ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; ​ /** * Created by zhangwd on 2019/7/15. */ public class BatisCache implements Cache {   private ReadWriteLock lock = new ReentrantReadWriteLock();   private ConcurrentHashMap<Object,Object> cache = new ConcurrentHashMap<Object, Object>();   private String id; ​   public BatisCache(){       System.out.println("初始化-1!");   } ​ //必须有该构造函数   public BatisCache(String id){       System.out.println("初始化-2!");       this.id = id;   } ​   // 获取缓存编号   public String getId() {       System.out.println("得到ID:" + id);       return id;   } ​   //获取缓存对象的大小   public int getSize() {       System.out.println("获取缓存大小!");       return 0;   }   // 保存key值缓存对象   public void putObject(Object key, Object value) {       System.out.println("往缓存中添加元素:key=" + key+",value=" + value);       cache.put(key,value);   } ​   //通过KEY   public Object getObject(Object key) {       System.out.println("通过kEY获取值:" + key);       System.out.println("OVER");       System.out.println("=======================================================");       System.out.println("值为:" + cache.get(key));       System.out.println("=====================OVER==============================");       return cache.get(key);   } ​   // 通过key删除缓存对象   public Object removeObject(Object key) {       System.out.println("移除缓存对象:" + key);       return null;   } ​   // 清空缓存   public void clear() {       System.out.println("清除缓存!");       cache.clear();   } ​   // 获取缓存的读写锁   public ReadWriteLock getReadWriteLock() {       System.out.println("获取锁对象!!!");       return lock;   } }

在Mapper文件里配置使用该自定义的缓存对象,如:

<cache type="com.sanyue.utils.BatisCache"/>

测试如下:

public static void main(String[] args) { ​       SqlSessionFactory factory = SqlSessionFactoryUtil.openSqlSession(); ​       // 获得SqlSession对象       SqlSession sqlSession = factory.openSession();       // 获得dao实体       UserMapper userMapper = sqlSession.getMapper(UserMapper.class);       // 进行两次相同的查询操作       userMapper.selectByPrimaryKey(1);       userMapper.selectByPrimaryKey(1);       // 注意,当我们使用二级缓存时候,sqlSession需要使用commit时候才会生效       sqlSession.commit(); ​       System.out.println("\n\n=============================================================");       // 获得一个新的SqlSession 对象       SqlSession sqlSession1 = factory.openSession();       // 进行相同的查询操作       sqlSession1.getMapper(UserMapper.class).selectByPrimaryKey(1);       sqlSession1.commit();   }

日志输出:

初始化-2! 得到ID:com.sanyue.dao.UserMapper 获取锁对象!!! 通过kEY获取值:151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647:       select       user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password       from user       where user_ID = ?   :1 OVER ======================================================= 值为:null =====================OVER============================== 获取锁对象!!! 获取锁对象!!! 通过kEY获取值:151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647:       select       user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password ​       from user       where user_ID = ?   :1 OVER ======================================================= 值为:null =====================OVER============================== 获取锁对象!!! 获取锁对象!!! 往缓存中添加元素:key=151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647:       select       user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password       from user       where user_ID = ?   :1,value=[User{userId=1, loginName='AS-01', password='12121212121', userName='小明', userCode='JSD-009', userType='ADMIN', userActive=true, userPosition='销售员'}] 获取锁对象!!! ============================================================= 获取锁对象!!! 通过kEY获取值:151355725:1423317450:com.sanyue.dao.UserMapper.selectByPrimaryKey:0:2147483647:       select       user_ID, login_name,user_name, user_code, user_type, user_active, organization_ID,user_position,password ​       from user       where user_ID = ?   :1 OVER ======================================================= 值为:[User{userId=1, loginName='AS-01', password='12121212121', userName='小明', userCode='JSD-009', userType='ADMIN', userActive=true, userPosition='销售员'}] =====================OVER============================== 获取锁对象!!!

 

可以看出,每次查询数据库前,MyBatis都会先在缓存中查找是否有该缓存对象。只有当调用了commit() 方法,MyBatis才会往缓存中写入数据,数据记录的键为 数字编号+Mapper名+方法名+SQL语句+参数 格式,值为返回的对象值。

5.1.3跟第三方内存缓存库集成(Ehcache)

MyBatis与Ehcache整合所需工具:https://gitee.com/sixiaojie/codes/hgumrf4k8ew2v1cjyqn7o57

1.加入ehcache包ehcache-core-2.6.5.jar和mybatis-ehcache-1.0.2.jar一个是ehcache自己的,一个是和mybatis的整合包

2.整合ehcache配置mapper中cache中的type为ehcache对cache接口的实现类型。我们在mybatis-ehcache-1.0.2.jar下找到org.mybatis.caches.ehcache包下有EhcacheCache.class类,这个就是ehcache整合mybatis的Cache接口的实现UserMapper.xml:

<mapper namespace="com.popo.UserMapper">     <!-- 开启本Mapper的namespace下的二级缓存   ​ type:执行cache接口实现类的类型,mybatis默认使用PerpatualCache,   ​ 要和ehcache整合,需要配置type为ehcache实现cache接口的类型-->   ​ <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>   ​ /*****************此处省略*******************/ ​ </mapper>

3.加入ehcache的配置文件 在classpath下配置ehcache.xml 代码如下:

<?xml version="1.0" encoding="UTF-8" ?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd" ​ <!-- 磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存 path:指定在硬盘上存储对象的路径 --> <!-- <diskStore path="java.io.tmpdir" /> --> ​ <!-- 指定磁盘文件夹存储缓存 --> <diskStore path="D:\ehcache3\ehcache-cache"/> ​ <!--   defaultCache:默认的缓存配置信息,如果不加特殊说明,则所有对象按照此配置项处理   maxElementsInMemory:设置了缓存的上限,最多存储多少个记录对象   eternal:代表对象是否永不过期   timeToIdleSeconds:最大的发呆时间   timeToLiveSeconds:最大的存活时间   overflowToDisk:是否允许对象被写入到磁盘   memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。           默认策略是LRU (last resently used 最近最少使用)。           你可以设置为FIFO(first in first out先进先出)           或是LFU(lest requently used 较少使用)。 --> <defaultCache   maxElementsInMemory="10000"   eternal="false"   timeToIdleSeconds="120"   timeToLiveSeconds="120"   overflowToDisk="true"   diskExpiryThreadIntervalSeconds="120"   memoryStoreEvictionPolicy="LRU" /> <!--   cache:为指定名称的对象进行缓存的特殊配置   name:指定对象的完整名 --> <cache name="com.zbaccp.entity.Person" maxElementsInMemory="10000" eternal="false"   timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> </ehcache>

 

4.ehcache.xml属性配置详解

1、diskStore :指定数据(.data and .index)存储位置,可指定磁盘中的文件夹位置

2、defaultCache : 默认的管理策略。

必须配置的:

  1、name: Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)。  2、maxElementsInMemory:在内存中缓存的element的最大数目。  3、maxElementsOnDisk:在磁盘上缓存的element的最大数目,默认值为0,表示不限制。  4、eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。  5、overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上。

可选配置:

1、timeToIdleSeconds: 对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。  

2、timeToLiveSeconds: 对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。  

3、diskPersistent: 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。  

4、diskExpiryThreadIntervalSeconds: 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。 

5、diskSpoolBufferSizeMB: DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。  

6、memoryStoreEvictionPolicy: 如果内存中数据超过内存限制,向磁盘缓存时的策略。默认值LRU,可选FIFO、LFU。

缓存的三种清空策略:

1、FIFO ,first in first out (先进先出).  

2、LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。  

3、LRU ,Least Recently Used(最近最少使用,即未被使用时间最长). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

                                                                                                                                                             --------郑重声明:本帖不得转载


最新回复(0)