Spring Boot+Mybatis Plus 使用Redis实现二级缓存

目录

一、Mybatis的缓存

二、使用Redis实现Mybatis二级缓存

配置application.yml文件

在启动类上添加@EnableCaching注解

设置RedisTemplate,在这可以对Redis的存储类型进行序列化,但是这里我没有使用

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

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

三、问题解决

applicationContext为Null



一、Mybatis的缓存

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

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

二、使用Redis实现Mybatis二级缓存

 首先先搭建一个spring boot项目,将依赖引入,我创建的是多项目,在父模块做了版本管理,这里继承父模块的版本,你们可按照需要引入自己的版本,要是项目不会创建可以参考我的另一篇文章https://blog.csdn.net/Cd_Air/article/details/118895785

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

配置application.yml文件

  #配置Tomcat端口
  server:
    port: 8090

  #配置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/wechatsubscription?useUnicode=true&characterEncoding=UTF-8
  #配置日志
  logging:
    level:
      root: info
      com.chenyx.mapper: debug
  #配置Mybatis-Plus
  mybatis-plus:
    mapper-locations: classpath:mybatis/mapper/*.xml
    configuration:
      cache-enabled: true
      map-underscore-to-camel-case: true

在启动类上添加@EnableCaching注解

package com.chenyx;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;


@MapperScan("com.chenyx.mapper")
@SpringBootApplication
@EnableCaching  // 开启缓存
public class MainApplication {

	public static void main(String[] args) {
		SpringApplication.run(MainApplication.class, args);
		System.out.println("Spring-Boot启动成功!");
	}

}

设置RedisTemplate,在这可以对Redis的存储类型进行序列化,但是这里我没有使用

package com.chenyx.config;

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;

/**
 * @Author 陈雲鑫
 * @Date 2021/7/24 17:23
 */
@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接口的实现

package com.chenyx.config;

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二级缓存
 *
 * @Author 陈雲鑫
 * @Date 2021/7/24 10:42
 */
@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

package com.chenyx.config;



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注解,就完成了

package com.chenyx.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chenyx.config.MybatisRedisCache;
import com.chenyx.entity.WeChatUser;
import org.apache.ibatis.annotations.CacheNamespace;

/**
 * @Author 陈雲鑫
 * @Date 2021/7/24 10:42
 */
@CacheNamespace(implementation= MybatisRedisCache.class,eviction=MybatisRedisCache.class)
public interface WeChatUserListMapper extends BaseMapper<WeChatUser>{
}

三、问题解决

applicationContext为Null

若创建的是多项目,ApplicationContextHolder工具类需要放在主项目中,不能放在别的工具类模块。

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
非常感谢您的提问。以下是 Spring Boot Mybatis Plus 使用 Redis 实现二级缓存的具体步骤和代码: 1. 首先,在 pom.xml 文件中添加 Redis 相关依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency> ``` 2. 在 application.properties 文件中添加 Redis 相关配置: ``` spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.database=0 spring.redis.password= spring.redis.timeout=3000 spring.redis.jedis.pool.max-active=8 spring.redis.jedis.pool.max-wait=-1 spring.redis.jedis.pool.max-idle=8 spring.redis.jedis.pool.min-idle=0 ``` 3. 在 Mybatis Plus 的配置文件中开启二级缓存,并配置 Redis 缓存: ``` @Configuration @MapperScan("com.example.mapper") public class MybatisPlusConfig { @Bean public ConfigurationCustomizer configurationCustomizer() { return new ConfigurationCustomizer() { @Override public void customize(Configuration configuration) { // 开启二级缓存 configuration.setCacheEnabled(true); // 配置 Redis 缓存 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)); // 设置缓存过期时间为 30 分钟 configuration.addCache(new RedisCache("mybatis-plus", new RedisCacheWriter() { @Override public void put(String key, byte[] value) { redisTemplate().opsForValue().set(key, value, Duration.ofMinutes(30)); } @Override public byte[] get(String key) { return redisTemplate().opsForValue().get(key); } @Override public void put(String key, byte[] value, long time, TimeUnit unit) { redisTemplate().opsForValue().set(key, value, Duration.ofMillis(unit.toMillis(time))); } @Override public void delete(String key) { redisTemplate().delete(key); } @Override public void clean() { redisTemplate().getConnectionFactory().getConnection().flushDb(); } @Override public long size() { return redisTemplate().getConnectionFactory().getConnection().dbSize(); } }, redisCacheConfiguration)); } }; } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); return redisTemplate; } @Bean public RedisConnectionFactory redisConnectionFactory() { LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(); lettuceConnectionFactory.setHostName("127.0.0.1"); lettuceConnectionFactory.setPort(6379); lettuceConnectionFactory.setPassword(""); lettuceConnectionFactory.setDatabase(0); return lettuceConnectionFactory; } } ``` 4. 在需要使用二级缓存的 Mapper 中添加 @CacheNamespace 注解: ``` @CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class) public interface UserMapper extends BaseMapper<User> { // ... } ``` 5. 最后,实现 MybatisRedisCache 类,继承自 RedisCache,重写 clear 方法: ``` public class MybatisRedisCache extends RedisCache { public MybatisRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration configuration) { super(name, cacheWriter, configuration); } @Override public void clear() { RedisConnection connection = Objects.requireNonNull(getRedisCacheWriter().getRedisConnectionFactory().getConnection()); connection.flushDb(); connection.close(); } } ``` 以上就是 Spring Boot Mybatis Plus 使用 Redis 实现二级缓存的具体步骤和代码。希望能对您有所帮助!
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值