SpringBoot整合MP通过Redis实现二级缓存方式
作者:龙域、白泽
这篇文章主要介绍了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>{ }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。