MyBatis框架(17):查询缓存-二级缓存-整合ehcache

mybatis整合ehcache
ehcache是一个分布式缓存框架。

1.分布缓存

我们系统为了提高系统并发,性能、一般对系统进行分布式部署(集群部署方式)
如图



不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统开发。所以要使用分布式缓存对缓存数据进行集中管理。

mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合。

2.整合方法(掌握)

mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。
mybatis和ehcache整合,mybatis和ehcache整合包中提供了一个cache接口的实现类。
mybatis默认实现cache类(接口)是:
此接口在MyBatis-3.2.7.jar下的org.apache.ibatis.cache包下的Cache.class

package org.apache.ibatis.cache;
 
import java.util.concurrent.locks.ReadWriteLock;
 
public interface Cache {
  
    //缓存在唯一标示
    String getId();
  
    //存入到缓存中
    void putObject(Object key, Object value);
 
    //根据key取出缓存
    Object getObject(Object key);
 
    //移除key 
    Object removeObject(Object key);
 
    void clear();
 
    int getSize();
  
    ReadWriteLock getReadWriteLock();
}

MyBatis默认实现支持的cache类是:

package org.apache.ibatis.cache.impl;
 
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
 
public class PerpetualCache implements Cache {
 
private String id;
  
private Map<Object, Object> cache = new HashMap<Object, Object>();
 
public PerpetualCache(String id) 
{
   
this.id = id;
  
}
 
public String getId() {
    
return id;
}
  
public int getSize() {
    
return cache.size();
  
}
 
public void putObject(Object key, Object value) {
   
 cache.put(key, value);
  
}
 
public Object getObject(Object key) {
    
return cache.get(key);
  
}
 
public Object removeObject(Object key) {
    
return cache.remove(key);
  
}
 
public void clear() {
    
cache.clear();
  
}
 
public ReadWriteLock getReadWriteLock() {
    
return null;
 
 }
 
 
 public boolean equals(Object o) {
    
if (getId() == null) 
throw new CacheException("Cache instances require an ID.");
    
if (this == o) return true;
    
if (!(o instanceof Cache)) return false;
    
Cache otherCache = (Cache) o;
    
return getId().equals(otherCache.getId());
  
}
 
public int hashCode() {
    
if (getId() == null) 
throw new CacheException("Cache instances require an ID.");
    
return getId().hashCode();
  }
 
}

3.加入ehcache包

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

4.整合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="cn.edu.hpu.mybatis.mapper.UserMapper">
 
    <!-- 开启本Mapper的namespace下的二级缓存 
    type:执行cache接口实现类的类型,mybatis默认使用PerpatualCache,
    要和ehcache整合,需要配置type为ehcache实现cache接口的类型-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    ......
</mapper>

5.加入ehcache的配置文件

在classpath下配置ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 缓存位置可以是自定义的硬盘地址也可以是JVM默认使用的缓存地址-->
    <diskStore path="F:\develop\ehcache" />
      <cache name="SimplePageCachingFilter"         
        maxElementsInMemory="10000"         
        eternal="false"        
        overflowToDisk="false"         
        timeToIdleSeconds="900"         
        timeToLiveSeconds="1800"        
        memoryStoreEvictionPolicy="LFU" />  

配置自定义缓存

  • name:Cache的唯一标识
  • maxElementsInMemory:缓存中允许创建的最大对象数
  • maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大
  • eternal:Element是否永久有效,一但设置了,timeout将不起作用,对象永不过期。
  • timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前, 两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效, 如果该值是 0 就意味着元素可以停顿无穷长的时间。
  • timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
  • overflowToDisk:内存不足时,是否启用磁盘缓存。
  • diskPersistent:是否缓存虚拟机重启期数据
  • diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
  • diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
  • memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)

测试:

不关闭SqlSession无法写进二级缓存区域中

//测试二级缓存
    @Test
    public void testCache2() throws Exception{
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
                //创建代理对象
        //下边查询使用一个SqlSession
        //第一次发起请求,查询id为1的用户
        User user1 = userMapper1.findUserById(1);
        System.out.println(user1.getUsername());
        //不关闭SqlSession无法写进二级缓存区域中
        sqlSession1.close();
        
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
                //创建代理对象
        //第二次发起请求,查询id为1的用户
        User user2 = userMapper2.findUserById(1);
        System.out.println(user2.getUsername());
        sqlSession2.close();
}

结果和输出日志:

DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 4554017.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@457d21]
DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
张明明
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@457d21]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@457d21]
DEBUG [main] - Returned connection 4554017 to pool.
DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.5
张明明

整合ehcache成功!

6.二级应用场景

对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

7.二级缓存局限性

mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容