Spring中使用Redis做為Mybatis二級(jí)緩存

Mybatis緩存通過(guò)org.apache.ibatis.cache.Cache實(shí)現(xiàn),利用Redis做Mybatis的二級(jí)緩存需要實(shí)現(xiàn)這個(gè)接口。

緩存過(guò)程中產(chǎn)生的key注意存儲(chǔ),flushCache的時(shí)候要?jiǎng)h除這些key對(duì)應(yīng)的緩存值。這里把這些key保存到為redis的list結(jié)構(gòu),以id作為list的key,每個(gè)mapper產(chǎn)生的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 二級(jí)緩存
 *
 * @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;

    /**
     * 這個(gè)地方需要靜態(tài)注入,這里通過(guò)中間類 MybatisRedisCacheTransfer 實(shí)現(xiàn)的
     * @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設(shè)置為立即過(guò)期
            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" 的時(shí)候會(huì)調(diào)用這個(gè)地方
     */
    @Override
    public void clear() {
        LOGGER.debug("mybatis redis cache clear. ");
        RedisConnection redisConnection = null;
        try {
            redisConnection = jedisConnectionFactory.getConnection();

            /**
             * 千萬(wàn)不要直接 redisConnection.flushDb(),會(huì)把整個(gè)redis的東西都清除掉,我不相信你的redis里沒(méi)有其他東西
             * 獲取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,幾種序列化方式比較可以參照:關(guān)于Spring Data redis幾種對(duì)象序列化的比較

clear()的時(shí)候千萬(wàn)不要直接 redisConnection.flushDb(),會(huì)把整個(gè)redis的東西都清除掉。


MybatisRedisCacheTransfer.java

package com.sdhs.mob.common.redis;

import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

/**
 * mybatis redis 二級(jí)緩存參數(shù)傳遞
 *
 * @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>

對(duì)MybatisRedisCache.java中的jedisConnectionFactory注入傳遞


Mybatis的配置和使用

mybatis-config.xml

<configuration>
    <settings>
    <!-- 啟動(dòng)二級(jí)緩存 -->
        <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的時(shí)候清空緩存
    -->
    <insert id="save"
            parameterType="xxx"
            useGeneratedKeys="true"
            flushCache="true"
            keyProperty="addrId">
        INSERT INTO Address
        <set>
            <include refid="setSql"/>
        </set>
    </insert>

    <!--
      useCache="true"
      select的時(shí)候使用緩存
    -->
    <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的時(shí)候可以觀察下兩次相同查詢時(shí)日志打印,查詢和修改時(shí)Redis里key的變化。

參考文檔:spring+springmvc+mybatis+redis實(shí)現(xiàn)緩存 - 甘雨路 - 博客園 感謝作者

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容