当前位置: 首页 > news >正文

JAVA面试宝典 - 《MyBatis 进阶:插件开发与二级缓存》

文章目录

  • 🚀《MyBatis 进阶:插件开发与二级缓存》
    • 一、开场引导:MyBatis 不只是 ORM 工具
    • 二、MyBatis 插件机制详解
      • 2.1 插件本质是“执行栈拦截器”
      • 2.2 插件开发实战:记录 SQL 执行耗时
        • 步骤一:实现 `Interceptor`
        • 步骤二:注册插件
      • 2.3 插件原理揭秘
    • 三、MyBatis 二级缓存机制
      • 3.1 一级缓存 vs 二级缓存
      • 3.2 启用二级缓存的步骤
    • 3.3 缓存失效与扩展
    • 四、实战案例:自定义 Cache 接入 Redis
      • 4.1 实现 Cache 接口
      • 4.2 注册自定义缓存
    • 五、常见问题与性能建议
    • 六、总结
    • 📚 推荐阅读

🚀《MyBatis 进阶:插件开发与二级缓存》

🎯 目标读者
已掌握 MyBatis 基础映射与 Mapper 使用,
想深入了解 MyBatis 插件机制与二级缓存原理及实践。


一、开场引导:MyBatis 不只是 ORM 工具

MyBatis 以 灵活可插拔轻量级 著称,但你是否真正了解它底层“魔法”?

  • 插件 能干预 SQL 执行流程:日志、权限、监控……
  • 二级缓存SqlSession 提升查询性能:内存、分布式都可扩展!

问题抛出:

  • 如何开发一个统计 SQL 执行耗时的插件?
  • 二级缓存如何从内存跑到 Redis?

二、MyBatis 插件机制详解

2.1 插件本质是“执行栈拦截器”

类比:Spring AOP 是“分层切面”,MyBatis 插件更像给执行栈每一站“排队拦车”——
每到关键节点,拦下并加入自定义逻辑,再放行。

可拦截的四大方法(对应核心四大处理器):

处理器可拦截方法
Executorquery()update()
ParameterHandlersetParameters()
ResultSetHandlerhandleResultSets()
StatementHandlerprepare()parameterize()batch()

2.2 插件开发实战:记录 SQL 执行耗时

步骤一:实现 Interceptor
@Intercepts({@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlTimingInterceptor implements Interceptor {@Overridepublic 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) { }
}
步骤二:注册插件
<plugins><plugin interceptor="com.example.mybatis.SqlTimingInterceptor"/>
</plugins>

2.3 插件原理揭秘

  1. Plugin.wrap(target, interceptor)
  • 如果目标类型匹配 @Signature,则用 JDK 动态代理或 CGLIB 生成代理对象。
  1. 链式拦截
  • 多个插件按配置顺序“嵌套”,先注册的最外层,最后执行的最内层。
  1. 执行流程
ExecutorImpl.query() → interceptor1.intercept() → interceptor2.intercept() → 实际 query 方法

三、MyBatis 二级缓存机制

3.1 一级缓存 vs 二级缓存

缓存类型范围生命周期默认开启可配置性
一级缓存单个 SqlSession会话内不可关闭
二级缓存Mapper 映射空间SqlSession可开启/禁用
  • 一级缓存:类似图书馆阅览室,只在“当天”可重复借阅。

  • 二级缓存:像书架借书,可以跨会话“长期”缓存(甚至跨进程)。

3.2 启用二级缓存的步骤

  1. Mapper 接口/映射文件 加上注解或 XML:
@CacheNamespace(implementation=RedisCache.class, eviction=FifoCache.class, flushInterval=60000)
public interface UserMapper { ... }
  1. 实体类实现 Serializable
  2. 查询方法返回可序列化对象,不能是流或 ResultHandler

写操作(insert/update/delete)会清空对应 namespace 下的二级缓存。

3.3 缓存失效与扩展

  • Session 关闭:一级缓存清空
  • 写操作:清空二级缓存
  • 命名空间隔离:不同 Mapper 之间不共享
  • 自定义缓存:可实现 org.apache.ibatis.cache.Cache 接口,用 Redis、Guava 等

四、实战案例:自定义 Cache 接入 Redis

4.1 实现 Cache 接口

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 注册自定义缓存

<cache type="com.example.mybatis.RedisCache"/>

发布到分布式集群时,所有节点都可共享同一 Redis 二级缓存。

五、常见问题与性能建议

问题建议
插件过多影响性能精简插件链,按需拦截;生产环境剔除非必要插件
二级缓存命中率低减少写操作频率;针对热点数据单独设计缓存清理策略
数据一致性难保证配合消息队列或 Redis 发布/订阅实现失效通知
统计所有 SQLExecutor.query()update() 中同时拦截

六、总结

  • 插件机制:通过 Interceptor 拦截执行栈,灵活扩展 SQL 监控、审计、权限等功能。
  • 二级缓存:跨会话缓存结果,默认内存实现,可自定义 Redis、Guava 等持久化方式。
  • 进阶能力:掌握插件与缓存设计,意味着你已步入 MyBatis 的“进阶圈”,能让 ORM 不止“映射”,还能“加速”与“智能化”!

📚 推荐阅读

  • MyBatis 官方文档:插件开发 & 缓存章节
  • MyBatis 源码深度解析
  • Redis 官方文档:Hash 结构与内存优化
http://www.dtcms.com/a/279189.html

相关文章:

  • 理解:进程、线程、协程
  • muduo面试准备
  • 文本预处理(四)
  • 2025-7-14-C++ 学习 排序(2)
  • 【LeetCode 热题 100】94. 二叉树的中序遍历——DFS
  • 死锁!哲学家进餐问题(操作系统os)
  • 光电融合新范式:长春光机所孙晓娟/李大冰团队《Light》发表铁电量子阱相纯度调控策略
  • 系统分析师第五课:进程通信-死锁-存储管理-固定分页分段
  • SpringMVC注解:@RequestParam 与 @PathVariable
  • 详解同步、异步、阻塞、非阻塞
  • 关于机械臂控制中的 MoveL 和 MoveJ 操作
  • Spring Boot + Thymeleaf + RESTful API 前后端整合完整示例
  • FBRT-YOLO: Faster and Better for Real-Time Aerial Image Detection论文精读(逐段解析)
  • linux服务器换ip后客户端无法从服务器下载数据到本地问题处理
  • 学生管理系统(C++实现)
  • 13.梯度scharr与lapkacia算子
  • 成都,工业设备边缘计算如何落地?——“边缘智能”新解法!
  • Linux入门:从文件存储到常用命令详解
  • 从数据库到播放器:Java视频续播功能完整实现解析
  • simscape中坐标系和坐标变换Frames and Transforms
  • MySQL数据实时同步到Elasticsearch的高效解决方案
  • 小波变换 | 连续小波变换
  • Effective Modern C++ 条款10:优先考虑限域enum而非未限域enum
  • 安全架构中身份与访问管理体系设计
  • 基于Yolov8车辆检测及图像处理系统【有代码】
  • python多版本管理--pyenv
  • pyspark中map算子和flatmap算子
  • RAG优化
  • Mysql数据库学习--约束
  • 聚宽sql数据库传递