用Redis做Mybatis二级缓存

首先在pom.xml文件里面添加依赖

用Redis做Mybatis二级缓存_第1张图片

 

然后再在application.yml

用Redis做Mybatis二级缓存_第2张图片

文件里面一旦有这个配置,你服务器启动时就会与redis做连接,所以启动服务器时一定要先启动redis

如果我们要对redis做用户控制的话,不然还要对它配置用户密码之类的

 

接下来我们再来做个缓存的实现,我们做个util包,在util包里面写个ApplicationContextHolder类来获取ApplicationContext(应用上下文,spring的核心对象)。所以这个类要继承ApplicationContextAware——当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。(相当于是自己包装了ApplicationContext的一个getBean方法)

package com.yy.hospital.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext ctx;

    @Override
    //向工具类注入applicationContext
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ctx = applicationContext;     //ctx就是注入的applicationContext
    }

    //外部调用ctx
    public static ApplicationContext getCtx(){
        return ctx;
    }


    //getbean有两种方式拿:1)按类型拿  2)按名字拿

    //从应用上下文里面获得类实例(即bean容器里面获得类容器)
    //为什么我们现在要采用这种麻烦的方法(以前直接用Autowired注解自动装配进去了)--- 这与redis连接池有关
    //用Redis时,建了许多连接池,我们在redis里面拿缓存对象时,缓存对象与每个连接都有一个RedisTemplate,你在注入时用自动注入,不同
    // RedisTemplate是同类型同名的,注入时你得到的是哪个连接使用的redisTemplate呢?所以你注入时分不清
    //所以我们重新封装一个getBean的方法,按指定类型或名字来拿bean实例
    public static  T getBean(Class tClass){
        return ctx.getBean(tClass);
    }

    @SuppressWarnings("unchecked")
    public static  T getBean(String name){
        return (T) ctx.getBean(name);
    }

}

ApplicationContextHolder是为接下来Mybatis的缓存类做准备的。所以我们来定义RedisCache类,来作为Mybatis二级缓存所使用的类。它要继承Cache接口(对Mybatis来说,你要用自定义的类来实现二级缓存,就要继承Mybatis的Cache接口)

package com.yy.hospital.util;

import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;


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

/*
    使用redis实现mybatis二级缓存
 */
public class RedisCache implements Cache {
    //slf4j的日志记录器
    private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
    //缓存对象唯一标识
    private final String id; //orm的框架都是按对象的方式缓存,而每个对象都需要一个唯一标识.
    //用于事务性缓存操作的读写锁
    private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //处理事务性缓存中做的
    //操作数据缓存的--跟着线程走的
    private  RedisTemplate redisTemplate;  //Redis的模板负责将缓存对象写到redis服务器里面去
    //缓存对象的是失效时间,30分钟
    private static final long EXPRIRE_TIME_IN_MINUT = 30;

    //构造方法---把对象唯一标识传进来
    public RedisCache(String id){
        if(id == null){
            throw new IllegalArgumentException("缓存对象id是不能为空的");
        }
        this.id = id;
    }

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


    //给模板对象RedisTemplate赋值,并传出去
    private RedisTemplate getRedisTemplate(){
        if(redisTemplate == null){    //每个连接池的连接都要获得RedisTemplate
            redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
        }
        return redisTemplate;
    }


    /*
        保存缓存对象的方法
     */
    @Override
    public void putObject(Object key, Object value) {
        try{
            RedisTemplate redisTemplate = getRedisTemplate();
            //使用redisTemplate得到值操作对象
            ValueOperations operation = redisTemplate.opsForValue();
            //使用值操作对象operation设置缓存对象
            operation.set(key,value,EXPRIRE_TIME_IN_MINUT, TimeUnit.MINUTES);  //TimeUnit.MINUTES系统当前时间的分钟数
            logger.debug("缓存对象保存成功");
        }catch (Throwable t){
            logger.error("缓存对象保存失败"+t);
        }

    }

    /*
        获取缓存对象的方法
     */
    @Override
    public Object getObject(Object key) {
        try {
            RedisTemplate redisTemplate = getRedisTemplate();
            ValueOperations operations = redisTemplate.opsForValue();
            Object result = operations.get(key);
            logger.debug("获取缓存对象");
            return result;
        }catch (Throwable t){
            logger.error("缓存对象获取失败"+t);
            return null;
        }
    }

    /*
        删除缓存对象
     */
    @Override
    public Object removeObject(Object key) {
        try{
            RedisTemplate redisTemplate = getRedisTemplate();
            redisTemplate.delete(key);
            logger.debug("删除缓存对象成功!");
        }catch (Throwable t){
            logger.error("删除缓存对象失败!"+t);
        }
        return null;
    }

    /*
        清空缓存对象
        当缓存的对象更新了的化,就执行此方法
     */
    @Override
    public void clear() {
        RedisTemplate redisTemplate = getRedisTemplate();
        //回调函数
        redisTemplate.execute((RedisCallback)collection->{
            collection.flushDb();
            return  null;
        });
        logger.debug("清空缓存对象成功!");
    }

    //可选实现的方法
    @Override
    public int getSize() {
        return 0;
    }

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



}

 

 

 

这样,二级缓存的工具类我们就写好了,接着我们要把工具类配置到映射器接口上去

第一种策略是直接在mapper接口上加上注解@CacheNamespace(implementation = 。。。。)

用Redis做Mybatis二级缓存_第3张图片

 

第二种策略就是在mapper.xml文件中加cache标签

用Redis做Mybatis二级缓存_第4张图片

记住,对于同一个mapper接口,他不能同时用两种策略(一个mapper里面同时用了注解SQL和.xml文件的SQL话,只能用其中一种策略)

 

 

最后,在启动类加上注解@EnableCaching就ok了

用Redis做Mybatis二级缓存_第5张图片

 

 

最后,我们通过数据库两次重复查询测试可以发现,redis里面已经存在缓存对象了(注意,缓存都是给查询用的)

用Redis做Mybatis二级缓存_第6张图片

 

 

 

有几个注意点

1)在内存中保存对象,对象要序列化

2)在.xml使用cache标签,只针对xml里的查询语句有用,所以针对要用缓存的查询,应该放在同一个mapper接口或者.xml文件中

3)使用了用户Token的登录相关的方法,最好不要做缓存操作

4)对缓存对象进行增删改操作,缓存对象会被清除掉

你可能感兴趣的:(SpringBoot,MySQL,项目)