• 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> {
    }
    
    • 1
    • 2
    • 3
    • 4

    开启所有的二级缓存:

    mybatis-plus:
        mapper-locations: classpath:mybatis/mapper/*.xml
        configuration:
          cache-enabled: true
    
    • 1
    • 2
    • 3
    • 4

    MybatisPlus整合Redis实现分布式二级缓存

    Mybatis内置的二级缓存在分布式环境下存在分布式问题,无法使用,但是我们可以整合Redis来实现分布式的二级缓存。

    1.引入依赖

    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
        <version>3.5.4.1version>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
    <dependency>
        <groupId>org.redissongroupId>
        <artifactId>redisson-spring-boot-starterartifactId>
        <version>3.24.3version>
    dependency>
    
    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
        <version>5.8.22version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    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;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    4.Mapper接口上开启二级缓存

    //开启二级缓存并指定缓存类
    @CacheNamespace(implementation = MybatisRedisCache.class,eviction = MybatisRedisCache.class)
    @Mapper
    public interface EmployeeMapper extends BaseMapper<Employee> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    看完这篇 教你玩转渗透测试靶机vulnhub——FunBox3(Easy)
    【Flink】处理迟到元素(续)、自定义水位线和多流的合并与合流
    OrcaTerm AI
    【面试题】2023虹软计算机视觉一面
    深度学习之情感分析
    目前工业界常用的推荐系统模型有哪些?
    SpringCloud-Docker原理解析
    使用正则表达式在中英文之间添加空格
    链表的带环问题
    C#.NET ORM 如何访问 Access 数据库 [FreeSql]
  • 原文地址:https://blog.csdn.net/dndndnnffj/article/details/134366561