MyBatis Plus整合Redis实现分布式二级缓存的问题
作者:爱码猿
Mybatis内置的二级缓存在分布式环境下存在分布式问题,无法使用,但是我们可以整合Redis来实现分布式的二级缓存,这篇文章给大家介绍MyBatis Plus整合Redis实现分布式二级缓存,感兴趣的朋友跟随小编一起看看吧
MyBatis缓存描述
MyBatis提供了两种级别的缓存, 分别时一级缓存和二级缓存。一级缓存是SqlSession级别的缓存,只在SqlSession对象内部存储缓存数据,如果SqlSession对象不一样就无法命中缓存,二级缓存是mapper级别的缓存,只要使用的Mapper类一样就能够共享缓存。
在查询数据时,Mybatis会优先查询二级缓存,如果二级缓存没有则查询一级缓存,都没有才会进行数据库查询。
Mybatis的一级缓存默认是开启的,而二级缓存需要在mapper.xml配置文件内或通过@CacheNamespace注解手动开启。
需要注意的是,在于Spring进行整合时,必须开启事务一级缓存会生效,因为不开启缓存的话每次查询都会重新创建一个SqlSession对象,因此无法共享缓存。
通过@CacheNamespace开启某个Mapper的二级缓存。
@Mapper @CacheNamespace public interface EmployeeMapper extends BaseMapper<Employee> { }
开启所有的二级缓存:
mybatis-plus: mapper-locations: classpath:mybatis/mapper/*.xml configuration: cache-enabled: true
MybatisPlus整合Redis实现分布式二级缓存
Mybatis内置的二级缓存在分布式环境下存在分布式问题,无法使用,但是我们可以整合Redis来实现分布式的二级缓存。
1.引入依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.4.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.24.3</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency>
2.配置RedisTemplate
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; @Configuration @EnableCaching public class RedisConfiguration { private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer(); private static final GenericJackson2JsonRedisSerializer JACKSON__SERIALIZER = new GenericJackson2JsonRedisSerializer(); @Bean @Primary public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { //设置缓存过期时间 RedisCacheConfiguration redisCacheCfg = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(STRING_SERIALIZER)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(JACKSON__SERIALIZER)); return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) .cacheDefaults(redisCacheCfg) .build(); } @Bean @Primary @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { // 配置redisTemplate RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); // key序列化 redisTemplate.setKeySerializer(STRING_SERIALIZER); // value序列化 redisTemplate.setValueSerializer(JACKSON__SERIALIZER); // Hash key序列化 redisTemplate.setHashKeySerializer(STRING_SERIALIZER); // Hash value序列化 redisTemplate.setHashValueSerializer(JACKSON__SERIALIZER); // 设置支持事务 redisTemplate.setEnableTransactionSupport(true); redisTemplate.afterPropertiesSet(); return redisTemplate; } @Bean public RedisSerializer<Object> redisSerializer() { //创建JSON序列化器 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //必须设置,否则无法将JSON转化为对象,会转化成Map类型 objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); return new GenericJackson2JsonRedisSerializer(objectMapper); } }
3.自定义缓存类
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.cache.Cache; import org.redisson.api.RReadWriteLock; import org.redisson.api.RedissonClient; import org.springframework.data.redis.connection.RedisServerCommands; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; @Slf4j public class MybatisRedisCache implements Cache { // redisson 读写锁 private final RReadWriteLock redissonReadWriteLock; // redisTemplate private final RedisTemplate redisTemplate; // 缓存Id private final String id; //过期时间 10分钟 private final long expirationTime = 1000*60*10; public MybatisRedisCache(String id) { this.id = id; //获取redisTemplate this.redisTemplate = SpringUtil.getBean(RedisTemplate.class); //创建读写锁 this.redissonReadWriteLock = SpringUtil.getBean(RedissonClient.class).getReadWriteLock("mybatis-cache-lock:"+this.id); } @Override public void putObject(Object key, Object value) { //使用redis的Hash类型进行存储 redisTemplate.opsForValue().set(getCacheKey(key),value,expirationTime, TimeUnit.MILLISECONDS); } @Override public Object getObject(Object key) { try { //根据key从redis中获取数据 Object cacheData = redisTemplate.opsForValue().get(getCacheKey(key)); log.debug("[Mybatis 二级缓存]查询缓存,cacheKey={},data={}",getCacheKey(key), JSONUtil.toJsonStr(cacheData)); return cacheData; } catch (Exception e) { log.error("缓存出错",e); } return null; } @Override public Object removeObject(Object key) { if (key != null) { log.debug("[Mybatis 二级缓存]删除缓存,cacheKey={}",getCacheKey(key)); redisTemplate.delete(key.toString()); } return null; } @Override public void clear() { log.debug("[Mybatis 二级缓存]清空缓存,id={}",getCachePrefix()); Set keys = redisTemplate.keys(getCachePrefix()+":*"); redisTemplate.delete(keys); } @Override public int getSize() { Long size = (Long) redisTemplate.execute((RedisCallback<Long>) RedisServerCommands::dbSize); return size.intValue(); } @Override public ReadWriteLock getReadWriteLock() { return this.redissonReadWriteLock; } @Override public String getId() { return this.id; } public String getCachePrefix(){ return "mybatis-cache:%s".formatted(this.id); } private String getCacheKey(Object key){ return getCachePrefix()+":"+key; } }
4.Mapper接口上开启二级缓存
//开启二级缓存并指定缓存类 @CacheNamespace(implementation = MybatisRedisCache.class,eviction = MybatisRedisCache.class) @Mapper public interface EmployeeMapper extends BaseMapper<Employee> { }
到此这篇关于MyBatis Plus整合Redis实现分布式二级缓存的文章就介绍到这了,更多相关MyBatis Plus整合Redis二级缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!