我的框架开发记录--2022.4.12

序言

昨天把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赋值。

如何使用缓存

好了,现在是配置好了,

但是在使用二级缓存时,也有问题。

先说说使用二级缓存的条件:

  1. 可以不开启配置文件中:cache-enabled: true,我看网上很多人说要开,我试了可以不开,默认就是开启的。。。

  2. mapper接口上使用@CacheNamespace(implementation = MybatisRedisCache.class),implementation 属性就是你自定义的cache实现类

  3. 在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上。但确实可以实现都走缓存

所以最终方案:

image-20220412222219511

,如果你某个sql不希望走二级缓存,可以在这个sql加上usecache="false"

image-20220412222340519
顺便提一下为什么把mybatis-plus的二级缓存改为redis实现,因为单纯的mybatis二级缓存是无法用于分布式的,服务于服务不能互相共享。二用redis缓存,那么就能有很多的空间和可能性。
好了,以上就是我踩得坑了。

猜你喜欢

转载自blog.csdn.net/qq_42682745/article/details/124136249