美文网首页SpringbootMybatis
Spring中使用Redis做为Mybatis二级缓存

Spring中使用Redis做为Mybatis二级缓存

作者: 不敢预言的预言家 | 来源:发表于2018-03-19 16:50 被阅读509次

    Mybatis缓存通过org.apache.ibatis.cache.Cache实现,利用Redis做Mybatis的二级缓存需要实现这个接口。

    缓存过程中产生的key注意存储,flushCache的时候要删除这些key对应的缓存值。这里把这些key保存到为redis的list结构,以id作为list的key,每个mapper产生的id不变。

    MybatisRedisCache.java

    package com.sdhs.mob.common.redis;
    
    import org.apache.ibatis.cache.Cache;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * mybatis redis 二级缓存
     *
     * @author seer
     * @date 2018/3/13 14:17
     */
    public class MybatisRedisCache implements Cache {
        private static Logger LOGGER = LogManager.getLogger(MybatisRedisCache.class);
        private final String id;
        private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        private static JedisConnectionFactory jedisConnectionFactory;
    
        /**
         * 这个地方需要静态注入,这里通过中间类 MybatisRedisCacheTransfer 实现的
         * @param jedisConnectionFactory
         */
        public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
            MybatisRedisCache.jedisConnectionFactory = jedisConnectionFactory;
        }
    
        public MybatisRedisCache(final String id) {
            if (null == id || "".equals(id)) {
                throw new IllegalArgumentException("mybatis redis cache need an id.");
            }
            this.id = id;
            LOGGER.debug("mybatis redis cache id: {}", id);
        }
    
    
        @Override
        public String getId() {
            return this.id;
        }
    
        /**
         * 存值
         * @param key
         * @param value
         */
        @Override
        public void putObject(Object key, Object value) {
            if (null == key) {
                return;
            }
            LOGGER.debug("mybatis redis cache put. K={} value={}", key, value);
            RedisConnection redisConnection = null;
            try {
                redisConnection = jedisConnectionFactory.getConnection();
                RedisSerializer serializer = new JdkSerializationRedisSerializer();
                redisConnection.set(serializer.serialize(key), serializer.serialize(value));
    
                // 将key保存到redis.list中
                redisConnection.lPush(serializer.serialize(id), serializer.serialize(key));
            } catch (Exception e) {
                LOGGER.error("mybatis redis cache put exception. K=" + key + " V=" + value + "", e);
            } finally {
                if (null != redisConnection) {
                    redisConnection.close();
                }
            }
        }
    
        /**
         * 取值
         * @param key
         * @return
         */
        @Override
        public Object getObject(Object key) {
            if (null == key) {
                return null;
            }
            LOGGER.debug("mybatis redis cache get. K={}", key);
            RedisConnection redisConnection = null;
            Object result = null;
            try {
                redisConnection = jedisConnectionFactory.getConnection();
                RedisSerializer serializer = new JdkSerializationRedisSerializer();
                result = serializer.deserialize(redisConnection.get(serializer.serialize(key)));
            } catch (Exception e) {
                LOGGER.error("mybatis redis cache get exception. K=" + key + " V=" + result + "", e);
            } finally {
                if (null != redisConnection) {
                    redisConnection.close();
                }
            }
            return result;
        }
    
        /**
         * 删值
         * @param key
         * @return
         */
        @Override
        public Object removeObject(Object key) {
            if (null == key) {
                return null;
            }
            LOGGER.debug("mybatis redis cache remove. K={}", key);
            RedisConnection redisConnection = null;
            Object result = null;
            try {
                redisConnection = jedisConnectionFactory.getConnection();
                RedisSerializer serializer = new JdkSerializationRedisSerializer();
                // 讲key设置为立即过期
                result = redisConnection.expireAt(serializer.serialize(key), 0);
                
                // 将key从redis.list中删除
                redisConnection.lRem(serializer.serialize(id), 0, serializer.serialize(key));
            } catch (Exception e) {
                LOGGER.error("mybatis redis cache remove exception. " + key + " V=" + result + "", e);
            } finally {
                if (null != redisConnection) {
                    redisConnection.close();
                }
            }
            return result;
        }
    
        /**
         * 清空缓存
         * flushCache="true" 的时候会调用这个地方
         */
        @Override
        public void clear() {
            LOGGER.debug("mybatis redis cache clear. ");
            RedisConnection redisConnection = null;
            try {
                redisConnection = jedisConnectionFactory.getConnection();
    
                /**
                 * 千万不要直接 redisConnection.flushDb(),会把整个redis的东西都清除掉,我不相信你的redis里没有其他东西
                 * 获取redis.list中的保存的key值,遍历删除
                 */
                Long length = redisConnection.lLen(serializer.serialize(id));
                if (0 == length) {
                    return;
                }
                List<byte[]> keyList = redisConnection.lRange(serializer.serialize(id), 0, length - 1);
                for (byte[] key : keyList) {
                    redisConnection.expireAt(key, 0);
                }
                redisConnection.expireAt(serializer.serialize(id), 0);
                keyList.clear();
            } catch (Exception e) {
                LOGGER.error("mybatis redis cache clear exception. ", e);
            } finally {
                if (null != redisConnection) {
                    redisConnection.close();
                }
            }
        }
    
        @Override
        public int getSize() {
            int result = 0;
            try {
                RedisConnection redisConnection = jedisConnectionFactory.getConnection();
                result = Math.toIntExact(redisConnection.dbSize());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
        @Override
        public ReadWriteLock getReadWriteLock() {
            return this.readWriteLock;
        }
    }
    
    

    类中采用了JdkSerializationRedisSerializer序列化Redis的key,几种序列化方式比较可以参照:关于Spring Data redis几种对象序列化的比较

    clear()的时候千万不要直接 redisConnection.flushDb(),会把整个redis的东西都清除掉。


    MybatisRedisCacheTransfer.java

    package com.sdhs.mob.common.redis;
    
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    
    /**
     * mybatis redis 二级缓存参数传递
     *
     * @author seer
     * @date 2018/3/13 14:17
     */
    public class MybatisRedisCacheTransfer {
        public void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {
            MybatisRedisCache.setJedisConnectionFactory(jedisConnectionFactory);
        }
    }
    
    <bean class="com.sdhs.mob.common.redis.MybatisRedisCacheTransfer">
        <property name="jedisConnectionFactory" ref="jedisConnectionFactory"/>
    </bean>
    

    MybatisRedisCache.java中的jedisConnectionFactory注入传递


    Mybatis的配置和使用

    mybatis-config.xml

    <configuration>
        <settings>
        <!-- 启动二级缓存 -->
            <setting name="cacheEnabled" value="true" />
        </settings>
    </configuration>
    

    在mapper.xml中使用

    <mapper namespace="com.sdhs.mapper.AddressMapper">
        <!-- 配置缓存类 -->
        <cache type="com.sdhs.mob.common.redis.MybatisRedisCache"/>
        <resultMap id="baseResultMap" type="xxx">
        </resultMap>
    
        <!--
          flushCache="true"
          insert、delete、update的时候清空缓存
        -->
        <insert id="save"
                parameterType="xxx"
                useGeneratedKeys="true"
                flushCache="true"
                keyProperty="addrId">
            INSERT INTO Address
            <set>
                <include refid="setSql"/>
            </set>
        </insert>
    
        <!--
          useCache="true"
          select的时候使用缓存
        -->
        <select id="list"
                parameterType="xxx"
                resultMap="baseResultMap"
                useCache="true">
            SELECT
            <include refid="colSql"/>
            FROM es_address
            <where>
                <include refid="whereSql"/>
            </where>
            ORDER BY modifyDate desc
            <include refid="limitSql"/>
        </select>
    <mapper>
    

    Test的时候可以观察下两次相同查询时日志打印,查询和修改时Redis里key的变化。

    参考文档:spring+springmvc+mybatis+redis实现缓存 - 甘雨路 - 博客园 感谢作者

    相关文章

      网友评论

      本文标题:Spring中使用Redis做为Mybatis二级缓存

      本文链接:https://www.haomeiwen.com/subject/yyspqftx.html