mybatisplus 配置二级缓存
MyBatis-Plus(简称 MP)作为 MyBatis 的增强工具,完全兼容 MyBatis 的二级缓存机制,同时提供了更便捷的配置方式。以下是基于 MyBatis-Plus 的二级缓存配置全指南,包含基础配置、高级特性及实战示例:
一、二级缓存核心依赖
确保项目中包含 MyBatis-Plus 及缓存相关依赖(以 Spring Boot 为例):
<!-- MyBatis-Plus核心依赖 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency><!-- 可选:Redis缓存(分布式环境必备) -->
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-redis</artifactId><version>1.0.0-beta2</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
二、基础配置(本地缓存)
适用于单机环境,使用 MyBatis 默认的内存缓存(PerpetualCache
)。
2.1 全局开启二级缓存
在application.yml
中开启全局缓存开关(MyBatis 默认开启,建议显式配置):
mybatis-plus:configuration:cache-enabled: true # 全局启用二级缓存(默认为true)mapper-locations: classpath*:mapper/**/*.xml # 指定Mapper.xml路径
2.2 实体类实现序列化
二级缓存存储对象需支持序列化(避免缓存失败):
package com.example.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;@Data
public class User implements Serializable {private static final long serialVersionUID = 1L; // 必须添加序列化版本号@TableId(type = IdType.AUTO)private Long id;private String username;private String email;private LocalDateTime createTime;
}
2.3 在 Mapper 中开启二级缓存
有两种方式配置 Mapper 级别的二级缓存,根据项目风格选择:
方式 1:XML 配置(推荐,支持更多缓存参数)
在对应的UserMapper.xml
中添加<cache>
标签:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.mapper.UserMapper"><!-- 配置二级缓存 --><cache eviction="LRU" <!-- 缓存淘汰策略:LRU(最近最少使用) -->flushInterval="60000" <!-- 自动刷新间隔(60秒) -->size="1024" <!-- 最大缓存对象数 -->readOnly="false"/> <!-- 非只读(需序列化) --><!-- MP自动生成的SQL会自动使用缓存,自定义SQL需显式配置 --><select id="selectById" resultType="com.example.entity.User">SELECT id, username, email, create_time AS createTime FROM t_user WHERE id = #{id}</select>
</mapper>
方式 2:注解配置(简化配置,适合无 XML 场景)
在 Mapper 接口上使用@CacheNamespace
注解:
package com.example.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.cache.impl.PerpetualCache;// 注解方式开启二级缓存,默认使用PerpetualCache
@CacheNamespace(eviction = LruCache.class, // 淘汰策略:LRUflushInterval = 60000, // 刷新间隔60秒size = 1024, // 最大缓存数readWrite = true // 读写模式(非只读)
)
public interface UserMapper extends BaseMapper<User> {// 继承BaseMapper的方法会自动使用缓存
}
三、高级配置(分布式缓存,Redis 为例)
单机缓存无法满足分布式环境需求,需集成 Redis 实现缓存共享。
3.1 配置 Redis 连接
创建redis.properties
(src/main/resources/):
redis.host=localhost
redis.port=6379
redis.timeout=2000
redis.password= # 无密码则留空
redis.database=0
redis.default.expiration=1800000 # 缓存默认过期时间(30分钟)
3.2 配置 Mapper 使用 Redis 缓存
方式 1:XML 配置指定 Redis 缓存类型
<mapper namespace="com.example.mapper.UserMapper"><!-- 使用Redis作为二级缓存 --><cache type="org.mybatis.caches.redis.RedisCache"><property name="eviction" value="LRU"/><property name="flushInterval" value="60000"/><property name="size" value="1024"/></cache>
</mapper>
方式 2:注解配置自定义 Redis 缓存
package com.example.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.CacheNamespace;
import org.mybatis.caches.redis.RedisCache;// 注解方式指定Redis缓存
@CacheNamespace(implementation = RedisCache.class)
public interface UserMapper extends BaseMapper<User> {
}
3.3 自定义 Redis 缓存(可选,优化序列化)
默认 Redis 缓存使用 Java 序列化,性能较差,可自定义 JSON 序列化:
package com.example.cache;import com.fasterxml.jackson.databind.ObjectMapper;
import org.mybatis.caches.redis.RedisCache;
import java.util.concurrent.locks.ReadWriteLock;public class JsonRedisCache extends RedisCache {private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();public JsonRedisCache(String id) {super(id);}@Overridepublic void putObject(Object key, Object value) {try {// 序列化为JSON字符串String jsonValue = OBJECT_MAPPER.writeValueAsString(value);super.putObject(key, jsonValue);} catch (Exception e) {throw new RuntimeException("缓存序列化失败", e);}}@Overridepublic Object getObject(Object key) {try {String jsonValue = (String) super.getObject(key);if (jsonValue != null) {// 反序列化为目标对象(根据实际类型调整)return OBJECT_MAPPER.readValue(jsonValue, User.class);}} catch (Exception e) {throw new RuntimeException("缓存反序列化失败", e);}return null;}// 禁用默认锁(Redis本身是单线程,无需额外锁)@Overridepublic ReadWriteLock getReadWriteLock() {return null;}
}
使用自定义缓存:
<cache type="com.example.cache.JsonRedisCache"/>
四、缓存细粒度控制
MyBatis-Plus 支持对单个方法配置缓存策略,覆盖全局设置。
4.1 禁用特定查询的缓存
<!-- XML方式:当前查询不使用二级缓存 -->
<select id="selectByUsername" resultType="User" useCache="false">SELECT * FROM t_user WHERE username = #{username}
</select>
// 注解方式:使用@Options禁用缓存
import org.apache.ibatis.annotations.Options;public interface UserMapper extends BaseMapper<User> {@Options(useCache = false)User selectByUsername(String username);
}
4.2 强制刷新缓存
<!-- 执行该查询前清空缓存 -->
<select id="selectLatestUsers" resultType="User" flushCache="true">SELECT * FROM t_user ORDER BY create_time DESC LIMIT 10
</select>
// 注解方式强制刷新
@Options(flushCache = Options.FlushCachePolicy.TRUE)
List<User> selectLatestUsers();
五、验证二级缓存生效
编写测试类验证缓存是否生效:
package com.example.test;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Objects;@Slf4j
@SpringBootTest
public class MpCacheTest {@Autowiredprivate SqlSessionFactory sqlSessionFactory;@Testpublic void testSecondLevelCache() {// 第一次查询:缓存未命中,查数据库try (SqlSession session1 = sqlSessionFactory.openSession()) {UserMapper mapper1 = session1.getMapper(UserMapper.class);User user1 = mapper1.selectOne(new QueryWrapper<User>().eq("id", 1L));log.info("第一次查询结果:{}", user1);} // 会话关闭,数据存入二级缓存// 第二次查询:命中二级缓存try (SqlSession session2 = sqlSessionFactory.openSession()) {UserMapper mapper2 = session2.getMapper(UserMapper.class);User user2 = mapper2.selectOne(new QueryWrapper<User>().eq("id", 1L));log.info("第二次查询结果:{}", user2);}}
}
预期日志(第二次查询无 SQL 输出,证明命中缓存):
第一次查询结果:User(id=1, username=test, ...)
==> Preparing: SELECT ... FROM t_user WHERE (id = ?)
==> Parameters: 1(Long)
<== Total: 1第二次查询结果:User(id=1, username=test, ...) // 无SQL执行,命中二级缓存
六、注意事项
BaseMapper 方法的缓存行为:
MyBatis-Plus 自动生成的 CRUD 方法(如selectById
、updateById
)会自动遵循缓存配置,更新操作会触发缓存清空。分页插件与缓存:
使用Page
分页查询时,缓存键会包含分页参数(pageNum
、pageSize
),确保不同分页的结果正确缓存。多表关联查询:
若 Mapper 中包含多表关联查询,建议使用@CacheNamespaceRef
关联相关 Mapper,确保更新任一表时同步清空缓存:@CacheNamespaceRef(UserMapper.class) // 共享UserMapper的缓存 public interface UserOrderMapper extends BaseMapper<UserOrder> { }
避免缓存超大结果集:
对返回大量数据的查询(如selectList()
无限制条件),建议禁用缓存(useCache=false
),避免内存溢出。
总结
MyBatis-Plus 配置二级缓存的核心是:
- 全局开启
cache-enabled: true
- 实体类实现
Serializable
- 在 Mapper 中通过 XML 或注解配置
<cache>
/@CacheNamespace
- 分布式环境替换为 Redis 缓存
通过合理配置,可充分利用二级缓存减少数据库访问,同时需平衡缓存命中率与数据一致性,避免脏读问题。