MyBatis整合Ehcache作为二级缓存

0、Ehcache简介

    Encache是一个纯粹的Java进程内的缓存框架,具有快速、精干等特点。具体来说,Encache主要特点如下。

  • 快速
  • 简单
  • 多种缓存策略(FIFO、LRU、LFU)
  • 缓存数据有内存和磁盘两级,无需担心容量问题
  • 缓存数据会在虚拟机重启的过程写入磁盘
  • 可以通过RMI、可插入API等方式进行分布式缓存
  • 具有缓存和缓存接口的侦听接口
       因为以上诸多优点,MyBatis项目开发者最早提供了Ehcache的MyBatis二级缓存实现,该项目名Ehcache-cache,EhCache官方网址是http://www.mybatis.org/ehcache-cache/。下面,我来演示一下在一个标准的Maven项目中集成EhCache框架。

1、添加Encache项目依赖

除了基本的MyBatis依赖、数据库驱动以外还需要在pom.xml中添加如下依赖:

<!--EnCache缓存核心包-->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.6</version>
</dependency>
<!--mybatis-ehcache整合包-->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>

2、配置ehcache.xml

2.1 ehcache缓存配置文件编写

       和MyBatis一样,EnCache也需要外部的配置文件,而且要求这个文件的名字必须是encache.xml,并且必须放在类路径的根目录下,即src/main/resources目录下

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">


<!--
        缓存配置
           diskStore:指定数据在磁盘中的存储位置。
           name:缓存名称。
           defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略,以下属性是必须的:
           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:内存数量最大时是否清除。
    -->

    <!-- 磁盘保存路径 -->
    <diskStore path="E:\ehcache"/>

    <defaultCache
            maxElementsInMemory="1"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            copyOnRead="true"
            copyOnWrite="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

       如果想增加一个针对某个MyBatis SQL映射文件的个性化缓存配置时,可以在ehcache.xml文件中添加一个和 SQL映射文件命名空间一致的缓存配置,例如针对UserMapper,可以进行如下配置:

<cache name="com.xust.iot.mapper.UserMapper"
           maxElementsInMemory="3000"
           maxElementsOnDisk="1000000"
           eternal="false"
           overflowToDisk="true"
           copyOnWrite="true"
           copyOnRead="true"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="3600"
           diskPersistent="true"/>
2.2 EhCache可配置信息总览
name Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)。
maxElementsInMemory 在内存中缓存的element的最大数目
maxElementsOnDisk 在磁盘上缓存的element的最大数目,默认值为0,表示不限制。
eternal 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。
overflowToDisk 如果内存中数据超过内存限制,是否要缓存到磁盘上。
copyOnRead判断从缓存中读取数据是否是返回对象的引用还是赋值一个对象返回。默认是false,即返回数据的引用,这种情况和MyBatis默认的缓存中只读对象是相同的。如果为true,那就是可读写缓存,每次读取缓存是都赋值一个新的实例。
copyOnWrite判断写入缓存时直接缓存对象的引用还是赋值一个对象后<缓存,默认值也是false。如果想使用可读写缓存,就需要将这两个属性配置为true,如果使用只读缓存,可以不配置这两个属性。
timeToIdleSeconds 对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。
timeToLiveSeconds 对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。
diskPersistent 是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。
diskExpiryThreadIntervalSeconds 对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。
diskSpoolBufferSizeMB DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。
memoryStoreEvictionPolicy 如果内存中数据超过内存限制,向磁盘缓存时的策略。默认值LRU,可选FIFO、LFU。
缓存清空策略 :
1、FIFO ,first in first out (先进先出).
2、LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。
3、LRU ,Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

3、修改MyBatis SQL映射文件

Ehcache提供了如下两个可选的缓存实现:
* org.mybatis.caches.ehcache.EhcacheCache
* org.mybatis.caches.ehcache.LoggingEhcache

       这两个缓存中,第二个是带日志的缓存,由于MyBatis初始化时,如果Cache不是继承自LoggingEhcache,MyBatis便会使用LoggingEhcache装饰代理缓存,所以上面两个缓存使用时并没有区别,都会输出命中率的日志。

修改后的UserMapper.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xust.iot.mapper.UserMapper">

    <!--在UserMapper中开启二级缓存,MyBatis支持集成第三方缓存,方法是使用type属性指定-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
    <select id="getUserByName" parameterType="string" resultType="user">
        select * from user
        <where>
          name=#{username}
        </where>
    </select>
</mapper>

在src/main/test目录下新建测试类AppTest.java测试是否用上了EhCache:


package test;

import com.xust.iot.beans.User;
import com.xust.iot.mapper.UserMapper;
import net.sf.ehcache.CacheEntry;
import net.sf.ehcache.Element;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.mybatis.caches.ehcache.EhcacheCache;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class AppTest {


    private static SqlSessionFactory sqlSessionFactory;
    private static Logger log = Logger.getLogger(AppTest.class);

    @Before
    public void init() {
        String resource = "mybatis-config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void test02() {
        //第一个会话
        SqlSession session = sqlSessionFactory.openSession(true);
        UserMapper sm = session.getMapper(UserMapper.class);
        List<User> user1 = sm.getUserByName("小明");
        if (null != user1 && user1.size() > 0) {
            for (User u : user1) {
                System.out.println(u.toString());
            }
        }
        session.close();

        //第二个会话
        System.out.println("开启新的会话......");
        SqlSession session2 = sqlSessionFactory.openSession(true);
        UserMapper sm2 = session2.getMapper(UserMapper.class);
        List<User> user2 = sm2.getUserByName("小明");
        if (null != user2 && user2.size() > 0) {
            for (User u : user2) {
                System.out.println(u.toString());
            }
        }
        session2.close();
    }
}

运行结果截图

在配置的磁盘路径下确实有缓存文件:

4、在SpringBoot 2.x上整合ehcache作为MyBatis二级缓存

       在SpringBoot上集成ehcache非常简单,其他的东西都一样,按照上面的方法配置即可,之后只需要在SpringBoot项目的配置文件中做如下配置:

spring: 
   cache:
    ehcache:
      config: classpath:ehcache-spring.xml   # ehcache缓存配置文件的位置

留言区

还能输入500个字符