使用Redis做Mybatis的二级缓存


前言

本篇记录怎么使用Redis做Mybtais的缓存。


一、二级缓存

MyBatis中的缓存分为一级缓存和二级缓存

  • 一级缓存:基于sqlSession的缓存
  • 二级缓存:基于多个sqlSession 共享的namspace数据块

通常一个mapper 都一个namespace,所有的相关二级缓存都存在该namespace 数据块下
查询顺序:先去二级缓存,如果二级没有再去一级缓存,一级没有再去数据库
注意:在spring 配置的mybatis 中不存在一级缓存,二级缓存发生增删改,该namespace 下所有缓存数据 立即清空,目的是为了避免有脏数据存在

二、使用步骤

创建项目,导入依赖和基本编码部分不再赘述

1.开启二级缓存

在配置文件yml中加入以下配置
在这里插入图片描述

代码如下:

    cache-enabled: true #使用缓存

2.编写ApplicationContextHolder

该类主要是为了在spring环境中获取非spring容器管理的bean

package com.lzl.secondcache;

import org.springframework.stereotype.Component;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.RedisTemplate;
/**
 * 在spring中,只要实现或者继承xxAware接口或者类,在实例该对象时,
 * 会调用实现xxAware接口的类的方法,把参数传递
 */
/**
 * --效率,是成功的核心关键--
 * 在spring中获取非spring管理的bean对象
 * @Author lzl
 * @Date 2023/3/9 08:00
 */
@Component
public class ApplicationContextHolder implements ApplicationContextAware {

    //spring容器
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextHolder.applicationContext = applicationContext;
    }

    public static RedisTemplate getRedisTemplate(){
        return ApplicationContextHolder.applicationContext
                .getBean("redisTemplate",RedisTemplate.class);
    }
}

3.编写RedisCache二级缓存工具类

package com.lzl.secondcache;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

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

/**
 * 数据查询顺序:二级缓存 -> 一级缓存 -> 数据库
 * 我们在mybatis中指定了二级缓存,在mybatis启动会生成Cache对象,
 * 如果在该类使用@Autowired注入RedisTemplate是无法注入的,需要使用spring注入
 */
/**
 * --效率,是成功的核心关键--
 *
 * @Author lzl
 * @Date 2023/3/9 08:02
 */

public class RedisCache implements Cache{
    //RedisTemplate对象
    private RedisTemplate redisTemplate;

    //id相当于当前sql对应的cache的命名空间 namespace="com.qf.mapper.xxxMapper"
    private String id;

    //读写锁:多线程中可以共享锁,如果大家都是读操作,提高数据的读的并发能力
    //如果有一个人进行了写操作,其他人都不能进行读写操作了
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //获取RedisTemplate对象
    public RedisTemplate getRedisTemplate(){
        //判断
        if(redisTemplate == null){
            synchronized (RedisCache.class){
                if(redisTemplate == null){
                    RedisTemplate redisTemplate = ApplicationContextHolder.getRedisTemplate();
                    //设置key使用string类型的序列化方式
                    redisTemplate.setKeySerializer(RedisSerializer.string());
                    return redisTemplate;
                }
                return this.redisTemplate;
            }

        }
        return redisTemplate;
    }

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

    //构造器
    public RedisCache(String id) {
        System.out.println("id:"+id);
        this.id = id;
    }

    //id相当于当前sql对应的cache的命名空间
    @Override
    public String getId() {
        System.out.println("getId:"+id);
        return id;
    }

    /**
     * 将结果放入缓存,当访问查询方法时调用,所以这里必须通过getRedisTemplate()方法来获取redisTemplate对象
     * @param key -> 命名空间 + sql + 参数 = 组成的字符串
     * @param value -> sql查询的结果
     */
    @Override
    public void putObject(Object key, Object value) {
        System.out.println("putObject中的key:"+key);
        System.out.println("putObject中的value:"+value);
        getRedisTemplate().opsForValue().set(key.toString(),value);
    }

    /**
     * 获取缓存中的数据,当访问查询方法时调用,所以这里必须通过getRedisTemplate()方法来获取redisTemplate对象
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        System.out.println("getObject:"+key);
        return getRedisTemplate().opsForValue().get(key.toString());
    }

    /**
     * 从缓存中移除数据,当访问查询方法时调用,所以这里必须通过getRedisTemplate()方法来获取redisTemplate对象
     * @param key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        System.out.println("removeObject:"+key);
        return getRedisTemplate().delete(key.toString());
    }

    /**
     * 清空缓存
     */
    @Override
    public void clear() {
        System.out.println("clear");
        Set keys = getRedisTemplate().keys("*" + id + "*");
        System.out.println("清空缓存keys:"+keys);
        getRedisTemplate().delete(keys);
    }

    /**
     * 获取缓存数据长度
     * @return
     */
    @Override
    public int getSize() {
        Set keys = getRedisTemplate().keys("*" + id + "*");
        return keys.size();
    }

}

4.在mapper.xml文件中开启全局二级缓存

    <!-- 开启二级缓存(全局) -->
    <!--
        type:使用自定义的对象进行存储
        blocking:true  查询时是否阻塞加锁
        flushInterval: 毫秒值,缓存多久清空一次
        eviction: 缓存失效策略: LRU – 最近最少使用:移除最长时间不被使用的对象。
                                FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
                                SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
                                WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
        readOnly:true 只读,不能被修改
        size: 1024 缓存的大小
    -->
    <cache type="com.lzl.secondcache.RedisCache"/>

5.配置RedisTemplate序列化工具类,实体也需要实现序列化接口

package com.lzl.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
 * --效率,是成功的核心关键--
 *
 * @Author lzl
 * @Date 2023/3/9 08:05
 */
@Configuration
public class RedisConfig {

    /**
     * springboot 默认帮我们创建的RedisTemplate的key和value的序列化方式是jdk默认的方式,
     * 我们有时候手动向redis中添加的数据可能无法被查询解析出来,所以我们需要修改序列化方式
     * @param connectionFactory
     * @return
     */

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 配置连接工厂
        redisTemplate.setConnectionFactory(connectionFactory);

        // 使用StringRedisSerializer来序列化和反序列化Redis的key值
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化Redis的value值
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // 配置对象映射器
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围。ANY指包括private和public修饰符范围
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入类型,类的信息也将添加到json中,这样才可以根据类名反序列化。
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        // 将对象映射器添加到序列化器中
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 配置key,value,hashKey,hashValue的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        return redisTemplate;
    }
}

需要注意的是参数列表会报一个无法自动注入的错误,属于正常现象,不影响代码正常运行

在这里插入图片描述

实体类需要实现序列化接口如下图所示

在这里插入图片描述

三、测试

首先我们先开启Redis,这里开虚拟机太麻烦,我使用了windows版的redis

在这里插入图片描述
在这里插入图片描述
启动成功

启动项目
在这里插入图片描述

启动成功,测试二级缓存的基本思路是,先调用查询的方法,查看redis中是否有缓存数据,再调用删除的方法,再次查看查看redis中的缓存数据是否被删除

先执行查询
在这里插入图片描述
查看控制台
在这里插入图片描述
我这里之前已经查过一次,所以直接调用了缓存,没有执行sql语句,如果是第一次访问,会先执行sql语句,再建立缓存
执行删除
在这里插入图片描述
查看控制台
在这里插入图片描述

删除成功!我们再次执行查询

在这里插入图片描述
查看控制台:
在这里插入图片描述
发现执行了sql语句
再次查询
在这里插入图片描述
缓存命中!大功告成!


总结

本篇简单记录一下springboot整合mybatis使用redis做二级缓存

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值