mybatis整合redis、ehcache实现二级缓存

目录

    • Mybatis集成ehcache
    • 整合redis

Mybatis集成ehcache

1. 为什么需要缓存
   拉高程序的性能
   
2. 什么样的数据需要缓存
   很少被修改或根本不改的数据
  
   业务场景比如:耗时较高的统计分析sql、电话账单查询sql等

3. ehcache是什么
   Ehcache 是现在最流行的纯Java开源缓存框架,配置简单、结构清晰、功能强大

4. ehcache的特点
  4.1 够快
      Ehcache的发行有一段时长了,经过几年的努力和不计其数的性能测试,Ehcache终被设计于large, high concurrency systems.
  4.2 够简单
      开发者提供的接口非常简单明了,从Ehcache的搭建到运用运行仅仅需要的是你宝贵的几分钟。其实很多开发者都不知道自己用在用Ehcache,Ehcache被广泛的运用于其他的开源项目
  4.3 够袖珍
      关于这点的特性,官方给了一个很可爱的名字small foot print ,一般Ehcache的发布版本不会到2M,V 2.2.3668KB。
  4.4 够轻量
      核心程序仅仅依赖slf4j这一个包,没有之一!
  4.5 好扩展
      Ehcache提供了对大数据的内存和硬盘的存储,最近版本允许多实例、保存对象高灵活性、提供LRU、LFU、FIFO淘汰算法,基础属性支持热配置、支持的插件多
  4.6 监听器
      缓存管理器监听器 (CacheManagerListener)和 缓存监听器(CacheEvenListener),做一些统计或数据一致性广播挺好用的
  4.7 分布式缓存
      从Ehcache 1.2开始,支持高性能的分布式缓存,兼具灵活性和扩展性

使用步骤:
导入相关依赖

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-context-supportartifactId>
    <version>${spring.version}version>
dependency>


<dependency>
    <groupId>org.mybatis.cachesgroupId>
    <artifactId>mybatis-ehcacheartifactId>
    <version>1.1.0version>
dependency>


<dependency>
    <groupId>net.sf.ehcachegroupId>
    <artifactId>ehcacheartifactId>
    <version>2.10.0version>
dependency>

修改日志配置,因为ehcache使用了Slf4j作为日志输出
日志我们使用slf4j,并用log4j来实现。SLF4J不同于其他日志类库,与其它有很大的不同。
SLF4J(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层( abstraction layer),
它允许你在后台使用任意一个日志类库。


    <log4j2.version>2.9.1log4j2.version>
    <log4j2.disruptor.version>3.2.0log4j2.disruptor.version>
    <slf4j.version>1.7.13slf4j.version>


    
    
    <dependency>
      <groupId>org.slf4jgroupId>
      <artifactId>slf4j-apiartifactId>
      <version>${slf4j.version}version>
    dependency>
    <dependency>
      <groupId>org.slf4jgroupId>
      <artifactId>jcl-over-slf4jartifactId>
      <version>${slf4j.version}version>
      <scope>runtimescope>
    dependency>

    
    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-apiartifactId>
      <version>${log4j2.version}version>
    dependency>
    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-coreartifactId>
      <version>${log4j2.version}version>
    dependency>
    
    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-slf4j-implartifactId>
      <version>${log4j2.version}version>
    dependency>
    
    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-webartifactId>
      <version>${log4j2.version}version>
      <scope>runtimescope>
    dependency>

    
    <dependency>
      <groupId>com.lmaxgroupId>
      <artifactId>disruptorartifactId>
      <version>${log4j2.disruptor.version}version>
    dependency>

在Resource中添加一个ehcache.xml的配置文件:


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    
    
    
    <diskStore path="java.io.tmpdir"/>


    
    
    
    
    
    
    
    
    
    
    
    <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
                  timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>

    
    
           
           
ehcache>

applicationContext.xml:


    <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml"/>
        <property name="shared" value="true">property>
    bean>
    
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="cacheManagerFactory"/>
    bean>

applicationContext-mybatis.xml中加入chache配置:


    <property name="configurationProperties">
        <props>
            
            <prop key="cacheEnabled">trueprop>
            
            <prop key="lazyLoadingEnabled">falseprop>
            
            <prop key="aggressiveLazyLoading">trueprop>
        props>
    property>


mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存:


    <property name="configurationProperties">
        <props>
            
            <prop key="cacheEnabled">trueprop>
            
            <prop key="lazyLoadingEnabled">falseprop>
            
            <prop key="aggressiveLazyLoading">trueprop>
        props>
    property>

在XxxMapper.xml中配置cache:

 
          
          
          
          
          
          
          <cache type="org.mybatis.caches.ehcache.EhcacheCache">
            <property name="timeToIdleSeconds" value="3600"/>
            <property name="timeToLiveSeconds" value="3600"/>
            <property name="maxEntriesLocalHeap" value="1000"/>
            <property name="maxEntriesLocalDisk" value="10000000"/>
            <property name="memoryStoreEvictionPolicy" value="LRU"/>
          cache>

通过select标签的useCache属性打开或关闭二级缓存:例如

 <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" useCache="false">select>

1、mybatis默认使用的二级缓存框架就是ehcache(org.mybatis.caches.ehcache.EhcacheCache),无缝结合
2、Mybatis缓存开关一旦开启,可缓存单条记录,也可缓存多条,hibernate不能缓存多条。
3、Mapper接口上的所有方法上另外提供关闭缓存的属性
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,
实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。

整合redis

  1. 添加jar依赖
 
    <redis.version>2.9.0redis.version>
    <redis.spring.version>1.7.1.RELEASEredis.spring.version>
    
    <dependency>
      <groupId>redis.clientsgroupId>
      <artifactId>jedisartifactId>
      <version>${redis.version}version>
    dependency>
    <dependency>
      <groupId>org.springframework.datagroupId>
      <artifactId>spring-data-redisartifactId>
      <version>${redis.spring.version}version>
    dependency>
  1. log4j2、jackson配置

    <jackson.version>2.9.3jackson.version> 


    <dependency>
      <groupId>com.fasterxml.jackson.coregroupId>
      <artifactId>jackson-databindartifactId>
      <version>${jackson.version}version>
    dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.coregroupId>
      <artifactId>jackson-coreartifactId>
      <version>${jackson.version}version>
    dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.coregroupId>
      <artifactId>jackson-annotationsartifactId>
      <version>${jackson.version}version>
    dependency>
  1. redis.properties
redis.hostName=xxx
redis.port=6379
redis.password=xxx
redis.timeout=10000
redis.maxIdle=300
redis.maxTotal=1000
redis.maxWaitMillis=1000
redis.minEvictableIdleTimeMillis=300000
redis.numTestsPerEvictionRun=1024
redis.timeBetweenEvictionRunsMillis=30000
redis.testOnBorrow=true
redis.testWhileIdle=true

  1. applicationContext-redis.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    
    

    
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        
        <property name="maxIdle" value="${redis.maxIdle}"/>
        
        <property name="maxTotal" value="${redis.maxTotal}"/>
        
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
        
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
        
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
        
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
        
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
        
        <property name="testWhileIdle" value="${redis.testWhileIdle}"/>
    bean>

    
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          destroy-method="destroy">
        <property name="poolConfig" ref="poolConfig"/>
        
        <property name="hostName" value="${redis.hostName}"/>
        
        <property name="port" value="${redis.port}"/>
        
        <property name="password" value="${redis.password}"/>
        
        <property name="timeout" value="${redis.timeout}"/>
    bean>

    
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        property>
        
        <property name="enableTransactionSupport" value="true"/>
    bean>

    
    <bean id="redisCacheTransfer" class="com.javaxl.ssm.util.RedisCacheTransfer">
        <property name="redisTemplate" ref="redisTemplate"/>
    bean>
beans>
  1. applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


<bean id="propertyConfigurer"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
    <property name="ignoreResourceNotFound" value="true" />
    <property name="locations">
        <list>
            <value>classpath:jdbc.propertiesvalue>
            <value>classpath:redis.propertiesvalue>
        list>
    property>
bean>

<import resource="applicationContext-mybatis.xml">import>


beans>
  1. 将redis缓存引入到mybatis中(创建mybatis的自定义缓存类“RedisCache”,必须实现org.apache.ibatis.cache.Cache接口)
package com.xiaoyang.ssm.util;


import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class RedisCache implements Cache //实现类
{
    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);

    private static RedisTemplate<String,Object> redisTemplate;

    private final String id;

    /**
     * The {@code ReadWriteLock}.
     */
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    @Override
    public ReadWriteLock getReadWriteLock()
    {
        return this.readWriteLock;
    }

    public static void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisCache.redisTemplate = redisTemplate;
    }

    public RedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        logger.debug("MybatisRedisCache:id=" + id);
        this.id = id;
    }

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public void putObject(Object key, Object value) {
        try{
            logger.info(">>>>>>>>>>>>>>>>>>>>>>>>putObject: key="+key+",value="+value);
            if(null!=value)
                redisTemplate.opsForValue().set(key.toString(),value,60, TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("redis保存数据异常!");
        }
    }

    @Override
    public Object getObject(Object key) {
        try{
            logger.info(">>>>>>>>>>>>>>>>>>>>>>>>getObject: key="+key);
            if(null!=key)
                return redisTemplate.opsForValue().get(key.toString());
        }catch (Exception e){
            e.printStackTrace();
            logger.error("redis获取数据异常!");
        }
        return null;
    }

    @Override
    public Object removeObject(Object key) {
        try{
            if(null!=key)
                return redisTemplate.expire(key.toString(),1,TimeUnit.DAYS);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("redis获取数据异常!");
        }
        return null;
    }

    @Override
    public void clear() {
        Long size=redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                Long size = redisConnection.dbSize();
                //连接清除数据
                redisConnection.flushDb();
                redisConnection.flushAll();
                return size;
            }
        });
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>clear: 清除了" + size + "个对象");
    }

    @Override
    public int getSize() {
        Long size = redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                return connection.dbSize();
            }
        });
        return size.intValue();
    }
}
  1. 静态注入中间类“RedisCacheTransfer”,解决RedisCache中RedisTemplate的静态注入,从而使MyBatis实现第三方缓存
package com.xiaoyang.ssm.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

public class RedisCacheTransfer {
    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisCache.setRedisTemplate(redisTemplate);
    }
}
  1. spring与mybatis整合文件中开发二级缓存

<property name="configurationProperties">
    <props>
        
        <prop key="cacheEnabled">trueprop>
        
        <prop key="lazyLoadingEnabled">falseprop>
        
        <prop key="aggressiveLazyLoading">trueprop>
    props>
property>
  1. 在XxxMapper.xml中添加自定义cache功能
<cache type="com.xiaoyang.ssm.util.RedisCache"
       eviction="LRU"
       flushInterval="6000000"
       size="1024"
       readOnly="false">cache>

type:指定redis的二级缓存类的全路径名
eviction:二级缓存算法(FIFO/LRU/SOFT/WEAK)
flushInterval:刷新间隔,间隔多长时间清空缓存,被动触发非定时器轮询
size:缓存大小。每个缓存可以存储 1024 个列表或对象的引用
readOnly:缓存将作为“读/写”缓存,意味着获取的对象不是共享的且对调用者是安全的。不会有其它的调用者或线程潜在修改。

over…

你可能感兴趣的:(mybatis,redis,ehcache)