首先在pom.xml文件里面添加依赖
然后再在application.yml
文件里面一旦有这个配置,你服务器启动时就会与redis做连接,所以启动服务器时一定要先启动redis
如果我们要对redis做用户控制的话,不然还要对它配置用户密码之类的
接下来我们再来做个缓存的实现,我们做个util包,在util包里面写个ApplicationContextHolder类来获取ApplicationContext(应用上下文,spring的核心对象)。所以这个类要继承ApplicationContextAware。(相当于是自己包装了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> T getBean(Class<T> tClass){
return ctx.getBean(tClass);
}
@SuppressWarnings("unchecked")
public static <T> 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 = 。。。。)
第二种策略就是在mapper.xml文件中加cache标签
记住,对于同一个mapper接口,他不能同时用两种策略(一个mapper里面同时用了注解SQL和.xml文件的SQL话,只能用其中一种策略)
最后,在启动类加上注解@EnableCaching就ok了
最后,我们通过数据库两次重复查询测试可以发现,redis里面已经存在缓存对象了(注意,缓存都是给查询用的)
有几个注意点
1)在内存中保存对象,对象要序列化
2)在.xml使用cache标签,只针对xml里的查询语句有用,所以针对要用缓存的查询,应该放在同一个mapper接口或者.xml文件中
3)使用了用户Token的登录相关的方法,最好不要做缓存操作
4)对缓存对象进行增删改操作,缓存对象会被清除掉