文章目录
- [🚀《MyBatis 进阶:插件开发与二级缓存》](#🚀《MyBatis 进阶:插件开发与二级缓存》)
-
- [一、开场引导:MyBatis 不只是 ORM 工具](#一、开场引导:MyBatis 不只是 ORM 工具)
- [二、MyBatis 插件机制详解](#二、MyBatis 插件机制详解)
-
- [2.1 插件本质是"执行栈拦截器"](#2.1 插件本质是“执行栈拦截器”)
- [2.2 插件开发实战:记录 SQL 执行耗时](#2.2 插件开发实战:记录 SQL 执行耗时)
-
- [步骤一:实现 `Interceptor`](#步骤一:实现
Interceptor
) - 步骤二:注册插件
- [步骤一:实现 `Interceptor`](#步骤一:实现
- [2.3 插件原理揭秘](#2.3 插件原理揭秘)
- [三、MyBatis 二级缓存机制](#三、MyBatis 二级缓存机制)
-
- [3.1 一级缓存 vs 二级缓存](#3.1 一级缓存 vs 二级缓存)
- [3.2 启用二级缓存的步骤](#3.2 启用二级缓存的步骤)
- [3.3 缓存失效与扩展](#3.3 缓存失效与扩展)
- [四、实战案例:自定义 Cache 接入 Redis](#四、实战案例:自定义 Cache 接入 Redis)
-
- [4.1 实现 Cache 接口](#4.1 实现 Cache 接口)
- [4.2 注册自定义缓存](#4.2 注册自定义缓存)
- 五、常见问题与性能建议
- 六、总结
- [📚 推荐阅读](#📚 推荐阅读)
🚀《MyBatis 进阶:插件开发与二级缓存》
🎯 目标读者
已掌握 MyBatis 基础映射与 Mapper 使用,
想深入了解 MyBatis 插件机制与二级缓存原理及实践。
一、开场引导:MyBatis 不只是 ORM 工具
MyBatis 以 灵活 、可插拔 、轻量级 著称,但你是否真正了解它底层"魔法"?
- 插件 能干预 SQL 执行流程:日志、权限、监控......
- 二级缓存 跨
SqlSession
提升查询性能:内存、分布式都可扩展!
问题抛出:
- 如何开发一个统计 SQL 执行耗时的插件?
- 二级缓存如何从内存跑到 Redis?
二、MyBatis 插件机制详解
2.1 插件本质是"执行栈拦截器"
类比:Spring AOP 是"分层切面",MyBatis 插件更像给执行栈每一站"排队拦车"------
每到关键节点,拦下并加入自定义逻辑,再放行。
可拦截的四大方法(对应核心四大处理器):
处理器 | 可拦截方法 |
---|---|
Executor |
query() 、update() |
ParameterHandler |
setParameters() |
ResultSetHandler |
handleResultSets() |
StatementHandler |
prepare() 、parameterize() 、batch() |
2.2 插件开发实战:记录 SQL 执行耗时
步骤一:实现 Interceptor
java
@Intercepts({
@Signature(type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlTimingInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.nanoTime();
Object result = invocation.proceed();
long elapsedMs = (System.nanoTime() - start) / 1_000_000;
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
if (elapsedMs > 100) {
System.err.printf("[Slow SQL] %s took %d ms%n", ms.getId(), elapsedMs);
}
return result;
}
@Override public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override public void setProperties(Properties props) { }
}
步骤二:注册插件
xml
<plugins>
<plugin interceptor="com.example.mybatis.SqlTimingInterceptor"/>
</plugins>
2.3 插件原理揭秘
- Plugin.wrap(target, interceptor)
- 如果目标类型匹配 @Signature,则用 JDK 动态代理或 CGLIB 生成代理对象。
- 链式拦截
- 多个插件按配置顺序"嵌套",先注册的最外层,最后执行的最内层。
- 执行流程
java
ExecutorImpl.query()
→ interceptor1.intercept()
→ interceptor2.intercept()
→ 实际 query 方法
三、MyBatis 二级缓存机制
3.1 一级缓存 vs 二级缓存
缓存类型 | 范围 | 生命周期 | 默认开启 | 可配置性 |
---|---|---|---|---|
一级缓存 | 单个 SqlSession |
会话内 | 是 | 不可关闭 |
二级缓存 | Mapper 映射空间 | 跨 SqlSession |
否 | 可开启/禁用 |
-
一级缓存:类似图书馆阅览室,只在"当天"可重复借阅。
-
二级缓存:像书架借书,可以跨会话"长期"缓存(甚至跨进程)。
3.2 启用二级缓存的步骤
- Mapper 接口/映射文件 加上注解或 XML:
java
@CacheNamespace(implementation=RedisCache.class, eviction=FifoCache.class, flushInterval=60000)
public interface UserMapper { ... }
- 实体类实现 Serializable
- 查询方法返回可序列化对象,不能是流或 ResultHandler
写操作(insert/update/delete)会清空对应 namespace 下的二级缓存。
3.3 缓存失效与扩展
- Session 关闭:一级缓存清空
- 写操作:清空二级缓存
- 命名空间隔离:不同 Mapper 之间不共享
- 自定义缓存:可实现 org.apache.ibatis.cache.Cache 接口,用 Redis、Guava 等
四、实战案例:自定义 Cache 接入 Redis
4.1 实现 Cache 接口
java
public class RedisCache implements Cache {
private final String id;
private RedisTemplate<String,Object> redis;
public RedisCache(String id) { this.id = id; }
@Override public String getId() { return id; }
@Override public void putObject(Object key, Object value) {
redis.opsForHash().put(id, key.toString(), value);
}
@Override public Object getObject(Object key) {
return redis.opsForHash().get(id, key.toString());
}
@Override public Object removeObject(Object key) {
return redis.opsForHash().delete(id, key.toString());
}
@Override public void clear() {
redis.delete(id);
}
@Override public int getSize() {
return redis.opsForHash().size(id).intValue();
}
}
4.2 注册自定义缓存
xml
<cache type="com.example.mybatis.RedisCache"/>
发布到分布式集群时,所有节点都可共享同一 Redis 二级缓存。
五、常见问题与性能建议
问题 | 建议 |
---|---|
插件过多影响性能 | 精简插件链,按需拦截;生产环境剔除非必要插件 |
二级缓存命中率低 | 减少写操作频率;针对热点数据单独设计缓存清理策略 |
数据一致性难保证 | 配合消息队列或 Redis 发布/订阅实现失效通知 |
统计所有 SQL | 在 Executor.query() 与 update() 中同时拦截 |
六、总结
- 插件机制:通过 Interceptor 拦截执行栈,灵活扩展 SQL 监控、审计、权限等功能。
- 二级缓存:跨会话缓存结果,默认内存实现,可自定义 Redis、Guava 等持久化方式。
- 进阶能力:掌握插件与缓存设计,意味着你已步入 MyBatis 的"进阶圈",能让 ORM 不止"映射",还能"加速"与"智能化"!
📚 推荐阅读
- MyBatis 官方文档:插件开发 & 缓存章节
- MyBatis 源码深度解析
- Redis 官方文档:Hash 结构与内存优化