序言
昨天把mybatis的二级缓存使用redis实现。
今天下班回家测试,发现一系列坑。
XpStart–2022.4.12
首先,需要说明的一点就是:
mybatis-plus使用redis做二级缓存和mybatis使用redis做二级缓存略有不同。
我按照之前的知识在plus中使用redis,启动的时候就会报错,空指针异常。
是哪里空指针呢?spring上下文没获取到为null。我在cache实现类拿不到redis的bean。
这里应该就是plus和mybatis二级缓存的区别吧:
mybatis貌似是在spring容器初始化后,mybatis才初始化cache实现类。
在plus中,会先实例化cache实现类,以至于用spring上下文此时为空,用它去拿redis的bean,自然就报空指针了。所以需要稍微改动:
Spring上下文工具类
@Configuration
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
//获取SpringBoot应用上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.context = applicationContext;
}
//获取bean对象
public static Object getBean(String beanName){
return context.getBean(beanName);
}
}
自定义缓存实现
/**
* mybatis二级缓存使用redis
* @author 29443
* @version 1.0
* @date 2022/4/11
*/
public class MybatisRedisCache implements Cache {
// 必须要定义一个id,以及id的构造函数(这个id是Mapper的namespace)
private final String id;
private RedisTemplate redisTemplate;
public MybatisRedisCache(String id) {
this.id = id;
System.out.println("id的值为:" + id);
// 因为Cache的实现类是mybatis实例化的,并不是由Spring容器管理。所以我们并不能直接注入RedisTemplate。
//redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
}
// getId()必须返回该id
@Override
public String getId() {
return id;
}
// 添加缓存
@Override
public void putObject(Object key, Object value) {
if (redisTemplate == null) {
redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
}
redisTemplate.opsForHash().put(id, key.toString(), value);
}
// 获取缓存
@Override
public Object getObject(Object key) {
if (redisTemplate == null) {
redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
}
return this.redisTemplate.opsForHash().get(id,key.toString());
}
// 从3.3.0开始,此方法仅在回滚期间调用
@Override
public Object removeObject(Object key) {
return null;
}
// 删除缓存
@Override
public void clear() {
if (redisTemplate == null) {
redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
}
redisTemplate.delete(id);
}
@Override
public int getSize() {
if (redisTemplate == null) {
redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
}
return Math.toIntExact(redisTemplate.opsForHash().size(id));
}
}
由于实现类初始化时获取不到redis的bean,所以只能在运行时用上面的工具类给redis赋值。
如何使用缓存
好了,现在是配置好了,
但是在使用二级缓存时,也有问题。
先说说使用二级缓存的条件:
-
可以不开启配置文件中:
cache-enabled: true
,我看网上很多人说要开,我试了可以不开,默认就是开启的。。。 -
mapper接口上使用@CacheNamespace(implementation = MybatisRedisCache.class),implementation 属性就是你自定义的cache实现类
-
在xml文件中写下:
<cache type="com.monkeylessey.redis.MybatisRedisCache"/>
type就是你自定义cache实现类的全类名。
ok,只要满足第二第三的任何一个,你都能使用缓存
但是!!!!!!!!!!!!!!!!!
区别来了:
@CacheNamespace
:只能用于注解形式的sql以及plus封装好的api,对于你自己在xml中写的sql,它是不会走缓存的!
<cache type="com.monkeylessey.redis.MybatisRedisCache"/>
:而它恰恰相反,它只会让xml中的sql走缓存。
这。。。。。。。。。。。。。。。
我全都要呢?
我在网上看到了一个解决方案,我也试了,确实可以:
@CacheNamespaceRef(name = "com.monkeylessey.mapper.SysUserMapper")
,使用这个注解替换@CacheNamespace
,同时xnl中也要有cache标签,@CacheNamespaceRef的name就是xml文件中的namespace。
挺有意思的,把自己的注解式sql和内置sql挂到自己的xml上。但确实可以实现都走缓存
所以最终方案:
,如果你某个sql不希望走二级缓存,可以在这个sql加上usecache="false"
顺便提一下为什么把mybatis-plus的二级缓存改为redis实现,因为单纯的mybatis二级缓存是无法用于分布式的,服务于服务不能互相共享。二用redis缓存,那么就能有很多的空间和可能性。
好了,以上就是我踩得坑了。