我的框架开发记录--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缓存,那么就能有很多的空间和可能性。
好了,以上就是我踩得坑了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了我的架构师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值