一级缓存与二级缓存
因为在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>{
- }