SpringBoot整合MP通过Redis实现二级缓存方式

 更新时间:2024年01月12日 16:01:53   作者:龙域、白泽  
这篇文章主要介绍了SpringBoot整合MP通过Redis实现二级缓存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一级缓存与二级缓存

  • 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 一级缓存是默认开启的
  • 二级缓存是namespace级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的

因为在Spring与Mybatis整合后,Mybatis的一级缓存是不能使用的,所以我们一般实现Mybatis的二级缓存,而在集群环境下,Mybatis的二级缓存只能实现单个节点的缓存,所以我们采用分布式的二级缓存,这里使用的是Redis的实现

配置文件

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
  #配置redis
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8
#配置Mybatis-Plus
mybatis-plus:
  mapper-locations: classpath:mybatis/mapper/*.xml
  configuration:
    cache-enabled: true

在启动类上添加@EnableCaching注解

EnableCaching:启动缓存功能

开启缓存功能,配置类中需要加上这个注解,有了这个注解以后,spring才知道你需要使用缓存的功能,其他的和缓存相关的注解才会有效,spring中主要是通过aop实现的,通过aop来拦截需要使用缓存的方法,实现缓存的功能

设置RedisTemplate

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
 
@Configuration
public class RedisConfiguration {
 
    /**
    * 设置redisTemplate
    */
    @Bean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

创建MybatisRedisCache类重写Mybatis二级缓存的Cache接口的实现

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
 
import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
/**
 *
 * 使用redis实现Mybatis Plus二级缓存
 *
 */
@Slf4j
public class MybatisRedisCache implements Cache {
 
 
    // 读写锁
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
 
    private RedisTemplate redisTemplate;
 
    private RedisTemplate getRedisTemplate(){
        //通过ApplicationContextHolder工具类获取RedisTemplate
        if (redisTemplate == null) {
            redisTemplate = (RedisTemplate) ApplicationContextHolder.getBeanByName("redisTemplate");
        }
        return redisTemplate;
    }
 
    private final String id;
 
    public MybatisRedisCache(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        this.id = id;
    }
 
    @Override
    public String getId() {
        return this.id;
    }
 
    @Override
    public void putObject(Object key, Object value) {
        //使用redis的Hash类型进行存储
        getRedisTemplate().opsForHash().put(id,key.toString(),value);
    }
 
    @Override
    public Object getObject(Object key) {
        try {
            //根据key从redis中获取数据
            return getRedisTemplate().opsForHash().get(id,key.toString());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("缓存出错 ");
        }
        return null;
    }
 
    @Override
    public Object removeObject(Object key) {
        if (key != null) {
            getRedisTemplate().delete(key.toString());
        }
        return null;
    }
 
    @Override
    public void clear() {
        log.debug("清空缓存");
        Set<String> keys = getRedisTemplate().keys("*:" + this.id + "*");
        if (!CollectionUtils.isEmpty(keys)) {
            getRedisTemplate().delete(keys);
        }
    }
 
    @Override
    public int getSize() {
        Long size = (Long) getRedisTemplate().execute((RedisCallback<Long>) RedisServerCommands::dbSize);
        return size.intValue();
    }
 
    @Override
    public ReadWriteLock getReadWriteLock() {
        return this.readWriteLock;
    }
}

因为RedisTemplate的实例化需要使用Spring的工厂进行创建,而我们创建的MybatisRedisCache类实现的是Mybatis的Cache接口,所以这个类不是由工厂进行管理的,所以我们不能直接在该类中直接使用注解注入RedisTemplate,所以我们创建一个获取Spring Boot创建好的工厂的ApplicationContextHolder工具类,用于获取RedisTemplate

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 applicationContext;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextHolder.applicationContext = applicationContext;
    }
 
    //根据bean name 获取实例
    public static Object getBeanByName(String beanName) {
        if (beanName == null || applicationContext == null) {
            return null;
        }
        return applicationContext.getBean(beanName);
    }
    //只适合一个class只被定义一次的bean(也就是说,根据class不能匹配出多个该class的实例)
    public static Object getBeanByType(Class clazz) {
        if (clazz == null || applicationContext == null) {
            return null;
        }
        return applicationContext.getBean(clazz);
    }
    public static String[] getBeanDefinitionNames() {
        return applicationContext.getBeanDefinitionNames();
    }
}

实现ApplicationContextAware接口后,在Spring Boot启动创建工厂后,就会自动调用这个接口的setApplicationContext方法,将创建的工厂以参数的形式传递给这个类,在这个方法中我们就可以把工厂给保存下来。

最后我们只需要在Mapper接口上添加@CacheNamespace注解,就完成了

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chenyx.config.MybatisRedisCache;
import com.chenyx.entity.WeChatUser;
import org.apache.ibatis.annotations.CacheNamespace;
 
 
@CacheNamespace(implementation= MybatisRedisCache.class,eviction=MybatisRedisCache.class)
public interface WeChatUserListMapper extends BaseMapper<WeChatUser>{
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java详解聊天窗口的创建流程

    Java详解聊天窗口的创建流程

    这篇文章主要介绍了怎么用Java来创建一个聊天窗口,聊天软件我们经常要用,但是你有想过自己怎么去实现它吗,感兴趣的朋友跟随文章往下看看吧
    2022-04-04
  • java根据不同的参数调用不同的实现类操作

    java根据不同的参数调用不同的实现类操作

    这篇文章主要介绍了java根据不同的参数调用不同的实现类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • springcloud安装rabbitmq并配置延迟队列插件的过程详解

    springcloud安装rabbitmq并配置延迟队列插件的过程详解

    本期主要讲解如何利用docker快速安装rabbitmq并且配置延迟队列插件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • Java实现银行存取款

    Java实现银行存取款

    这篇文章主要为大家详细介绍了Java实现银行存取款,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • SpringBoot的@RestControllerAdvice作用详解

    SpringBoot的@RestControllerAdvice作用详解

    这篇文章主要介绍了SpringBoot的@RestControllerAdvice作用详解,@RestContrllerAdvice是一种组合注解,由@ControllerAdvice,@ResponseBody组成,本质上就是@Component,需要的朋友可以参考下
    2024-01-01
  • Spring之从桥接方法到JVM方法调用解读

    Spring之从桥接方法到JVM方法调用解读

    这篇文章主要介绍了Spring之从桥接方法到JVM方法调用解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • Springboot打包为Docker镜像并部署的实现

    Springboot打包为Docker镜像并部署的实现

    这篇文章主要介绍了Springboot打包为Docker镜像并部署的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 分享15款Java程序员必备的开发工具

    分享15款Java程序员必备的开发工具

    这篇文章主要介绍了分享15款Java程序员必备的开发工具,需要的朋友可以参考下
    2015-02-02
  • SpringBoot接口正确接收时间参数的几种方式

    SpringBoot接口正确接收时间参数的几种方式

    这篇文章主要给大家介绍了关于SpringBoot接口正确接收时间参数的相关资料,文中通过代码示例介绍的非常详细,对大家学习或者使用springboot具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • Java比较两个List的值是否相等的方法

    Java比较两个List的值是否相等的方法

    这篇文章主要介绍了Java比较两个List的值是否相等的方法,涉及java针对队列比较的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07

最新评论