基于Redis的MybatisPlus二级缓存解决方案

 更新时间:2026年07月10日 09:07:54   作者:xiatianit  
在Mybatis-Plus中使用二级缓存通常是为了提高查询效率,特别是在多次查询相同数据时,Mybatis-Plus本身并不直接支持集成Redis作为二级缓存的解决方案,本文介绍MybatisPlus二级缓存Redis,感兴趣的朋友一起看看吧

在 MyBatis-Plus 中整合 Redis 作为二级缓存,核心在于‌正确配置 RedisTemplate 的序列化方式‌以及‌实现 MyBatis 的 Cache 接口‌。由于 MyBatis 的 Cache 实现类不由 Spring 容器直接管理,因此需要特殊处理以获取 RedisTemplate

以下是完整的配置示例与代码实现:

1. 引入依赖

确保项目中包含 MyBatis-Plus 和 Spring Data Redis 依赖:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version> <!-- 建议使用较新版本 -->
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 可选:用于方便地从非Spring管理的类中获取Bean -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.22</version>
</dependency>

2. 配置 RedisTemplate (关键步骤)

MyBatis 缓存的对象必须可序列化。推荐使用 ‌JSON 序列化‌(如 Jackson),因为它比 JDK 默认序列化更节省空间且可读性更好。

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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        // 使用 String 序列化 Key
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        // 使用 Jackson 序列化 Value
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 解决 java.util.Date 等类型序列化问题,并启用默认类型信息以支持多态
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

3. 实现 MyBatis Cache 接口

创建一个自定义类实现 org.apache.ibatis.cache.Cache 接口。注意:该类由 MyBatis 创建,而非 Spring,因此不能直接 @Autowired。需通过 ApplicationContext 获取 RedisTemplate

方案 A:使用 Hutool 的 SpringUtil(推荐,简洁)

import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Slf4j
public class MybatisRedisCache implements Cache {
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final String id;
    private RedisTemplate<String, Object> redisTemplate;
    // 缓存过期时间(分钟),可根据业务调整
    private static final long EXPIRE_TIME_IN_MINUTES = 30;
    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) {
        getRedisTemplate().opsForHash().put(getId(), key.toString(), value);
        log.debug("Put query result to redis: key={}", key);
    }
    @Override
    public Object getObject(Object key) {
        log.debug("Get cached query result from redis: key={}", key);
        return getRedisTemplate().opsForHash().get(getId(), key.toString());
    }
    @Override
    public Object removeObject(Object key) {
        log.debug("Remove cached query result from redis: key={}", key);
        return getRedisTemplate().opsForHash().delete(getId(), key.toString());
    }
    @Override
    public void clear() {
        log.debug("Clear all cached query results from redis for namespace: {}", getId());
        getRedisTemplate().delete(getId());
    }
    @Override
    public int getSize() {
        Long size = getRedisTemplate().opsForHash().size(getId());
        return size == null ? 0 : size.intValue();
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }
    private RedisTemplate<String, Object> getRedisTemplate() {
        if (redisTemplate == null) {
            // 从 Spring 容器中获取 RedisTemplate
            redisTemplate = SpringUtil.getBean("redisTemplate");
        }
        return redisTemplate;
    }
}

方案 B:使用静态 ApplicationContext 持有者(如果不使用 Hutool)
若不使用 Hutool,需创建一个 SpringContextHolder 实现 ApplicationContextAware 接口来静态获取 Bean,然后在 MybatisRedisCache 中调用 SpringContextHolder.getBean("redisTemplate")

4. 开启二级缓存

全局配置 (application.yml)

mybatis-plus:
  configuration:
    cache-enabled: true # 全局开启二级缓存
    # 其他配置...

Mapper 级别配置

在具体的 Mapper 接口或 XML 中指定使用自定义的 Redis 缓存。

方式一:注解方式(推荐)

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.CacheNamespace;
@CacheNamespace(implementation = MybatisRedisCache.class)
public interface UserMapper extends BaseMapper<User> {
    // 查询方法
}

方式二:XML 方式
在对应的 UserMapper.xml 文件中添加:

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 指定自定义的 Redis 缓存实现类 -->
    <cache type="com.example.config.MybatisRedisCache"/>
    <!-- 你的 SQL 语句 -->
    <select id="selectById" resultType="com.example.entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

5. 注意事项

  1. 实体类序列化‌:所有存入缓存的实体类(如 User)必须实现 Serializable 接口,否则 JSON 序列化可能出错或无法反序列化。
  2. 缓存一致性‌:MyBatis 二级缓存默认在 insert/update/delete 操作后会清空该 Namespace 下的所有缓存。在分布式高并发场景下,这可能不是最优策略,建议结合业务需求考虑是否在服务层使用 Spring Cache (@Cacheable) 替代 MyBatis 二级缓存,以获得更细粒度的控制。
  3. Key 的设计‌:上述示例使用 opsForHash 将同一 Mapper Namespace 下的所有缓存项存储在一个 Hash 结构中,Key 为查询条件的哈希或字符串。这有助于管理和清理特定 Mapper 的缓存。
  4. 一级缓存干扰‌:在 Spring 集成环境中,MyBatis 的一级缓存(SqlSession 级别)通常因 SqlSession 的生命周期管理而效果有限,二级缓存才是跨 SqlSession 共享的关键。确保 cache-enabled: true 已设置。

到此这篇关于基于Redis的MybatisPlus二级缓存解决方案的文章就介绍到这了,更多相关MybatisPlus Redis 二级缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Java内存区域与对象创建过程

    浅谈Java内存区域与对象创建过程

    下面小编就为大家带来一篇浅谈Java内存区域与对象创建过程。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Java 实战项目锤炼之IT设备固定资产管理系统的实现流程

    Java 实战项目锤炼之IT设备固定资产管理系统的实现流程

    读万卷书不如行万里路,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,本篇文章手把手带你用Java+SSM+jsp+mysql+maven实现一个IT设备固定资产管理系统,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • Spring 4 支持的 Java 8 特性

    Spring 4 支持的 Java 8 特性

    Spring 框架 4 支持 Java 8 语言和 API 功能。在本文中,我们将重点放在 Spring 4 支持新的 Java 8 的功能。最重要的是 Lambda 表达式,方法引用,JSR-310的日期和时间,和可重复注释。下面跟着小编一起来看下吧
    2017-03-03
  • springboot项目中idea的pom.xml文件的引用标签全部爆红问题解决

    springboot项目中idea的pom.xml文件的引用标签全部爆红问题解决

    这篇文章主要介绍了springboot项目中idea的pom.xml文件的引用标签全部爆红问题解决,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧
    2023-12-12
  • SpringCloud Gateway实现请求解密和响应加密的过程解析

    SpringCloud Gateway实现请求解密和响应加密的过程解析

    这篇文章主要介绍了SpringCloud Gateway实现请求解密和响应加密的相关知识,本文环境使用比较新的 Java 17 和 SpringBoot 3.1.5,对应到Spring的版本是 6.0.13,本文重心是网关项目,需要的朋友可以参考下
    2023-11-11
  • SpingMvc复杂参数传收总结

    SpingMvc复杂参数传收总结

    这篇文章主要为大家介绍了SpingMvc复杂参数传收总结,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Java中的WeakHashMap源码分析

    Java中的WeakHashMap源码分析

    这篇文章主要介绍了Java中的WeakHashMap源码分析,WeakHashMap可能平时使用的频率并不高,但是你可能听过WeakHashMap会进行自动回收吧,下面就对其原理进行分析,需要的朋友可以参考下
    2023-09-09
  • java compiler没有1.8怎么解决

    java compiler没有1.8怎么解决

    这篇文章主要介绍了java compiler没有1.8的解决方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-08-08
  • Java Socket编程实例(一)- TCP基本使用

    Java Socket编程实例(一)- TCP基本使用

    这篇文章主要讲解Java Socket编程中TCP的基本使用,希望能给大家做一个参考。
    2016-06-06
  • Java使用Hutool实现AES、DES加密解密的方法

    Java使用Hutool实现AES、DES加密解密的方法

    本篇文章主要介绍了Java使用Hutool实现AES、DES加密解密的方法,具有一定的参考价值,有兴趣的可以了解一下
    2017-08-08

最新评论