📌 专栏连载 :《2026全新 | Java 100 天进阶之路:从零基础到上岗就业,108篇完整学习地图》
⬅️ 上一篇:第89篇:MySQL面试压轴题 |
➡️ 下一篇:第91篇:Redis核心数据结构
文章目录
-
-
- [一、MyBatis SQL完整执行流程链路图](#一、MyBatis SQL完整执行流程链路图)
- 二、基础概念篇(5道高频题)
- 三、核心SQL配置篇(8道真题)
- 四、Mapper动态代理源码篇(5题)
- 五、缓存机制篇(5题)
- 六、Spring整合篇(3题)
- 七、MyBatis-Plus全考点(6题)
- 八、生产环境避坑汇总表
- 九、课后面试真题练习
- [📊 你的学习进度](#📊 你的学习进度)
- [👉 下一篇文章预告](#👉 下一篇文章预告)
-
一、MyBatis SQL完整执行流程链路图
💡 重点:面试官问执行流程时,直接画出这张图,秒杀80%只会背文字的竞争者!
#mermaid-svg-MzpWqJMCQRo3gghe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MzpWqJMCQRo3gghe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MzpWqJMCQRo3gghe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MzpWqJMCQRo3gghe .error-icon{fill:#552222;}#mermaid-svg-MzpWqJMCQRo3gghe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MzpWqJMCQRo3gghe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MzpWqJMCQRo3gghe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MzpWqJMCQRo3gghe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MzpWqJMCQRo3gghe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MzpWqJMCQRo3gghe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MzpWqJMCQRo3gghe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MzpWqJMCQRo3gghe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MzpWqJMCQRo3gghe .marker.cross{stroke:#333333;}#mermaid-svg-MzpWqJMCQRo3gghe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MzpWqJMCQRo3gghe p{margin:0;}#mermaid-svg-MzpWqJMCQRo3gghe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MzpWqJMCQRo3gghe .cluster-label text{fill:#333;}#mermaid-svg-MzpWqJMCQRo3gghe .cluster-label span{color:#333;}#mermaid-svg-MzpWqJMCQRo3gghe .cluster-label span p{background-color:transparent;}#mermaid-svg-MzpWqJMCQRo3gghe .label text,#mermaid-svg-MzpWqJMCQRo3gghe span{fill:#333;color:#333;}#mermaid-svg-MzpWqJMCQRo3gghe .node rect,#mermaid-svg-MzpWqJMCQRo3gghe .node circle,#mermaid-svg-MzpWqJMCQRo3gghe .node ellipse,#mermaid-svg-MzpWqJMCQRo3gghe .node polygon,#mermaid-svg-MzpWqJMCQRo3gghe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MzpWqJMCQRo3gghe .rough-node .label text,#mermaid-svg-MzpWqJMCQRo3gghe .node .label text,#mermaid-svg-MzpWqJMCQRo3gghe .image-shape .label,#mermaid-svg-MzpWqJMCQRo3gghe .icon-shape .label{text-anchor:middle;}#mermaid-svg-MzpWqJMCQRo3gghe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MzpWqJMCQRo3gghe .rough-node .label,#mermaid-svg-MzpWqJMCQRo3gghe .node .label,#mermaid-svg-MzpWqJMCQRo3gghe .image-shape .label,#mermaid-svg-MzpWqJMCQRo3gghe .icon-shape .label{text-align:center;}#mermaid-svg-MzpWqJMCQRo3gghe .node.clickable{cursor:pointer;}#mermaid-svg-MzpWqJMCQRo3gghe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MzpWqJMCQRo3gghe .arrowheadPath{fill:#333333;}#mermaid-svg-MzpWqJMCQRo3gghe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MzpWqJMCQRo3gghe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MzpWqJMCQRo3gghe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MzpWqJMCQRo3gghe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MzpWqJMCQRo3gghe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MzpWqJMCQRo3gghe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MzpWqJMCQRo3gghe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MzpWqJMCQRo3gghe .cluster text{fill:#333;}#mermaid-svg-MzpWqJMCQRo3gghe .cluster span{color:#333;}#mermaid-svg-MzpWqJMCQRo3gghe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MzpWqJMCQRo3gghe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MzpWqJMCQRo3gghe rect.text{fill:none;stroke-width:0;}#mermaid-svg-MzpWqJMCQRo3gghe .icon-shape,#mermaid-svg-MzpWqJMCQRo3gghe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MzpWqJMCQRo3gghe .icon-shape p,#mermaid-svg-MzpWqJMCQRo3gghe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MzpWqJMCQRo3gghe .icon-shape .label rect,#mermaid-svg-MzpWqJMCQRo3gghe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MzpWqJMCQRo3gghe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MzpWqJMCQRo3gghe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MzpWqJMCQRo3gghe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 加载配置构建SqlSessionFactory
openSession获取SqlSession
getMapper生成MapperProxy代理
调用Mapper接口方法
MapperMethod分发SQL类型
Executor先查询一二级缓存
StatementHandler创建PreparedStatement
ParameterHandler填充占位符
执行JDBC查询
ResultSetHandler映射实体返回
二、基础概念篇(5道高频题)
Q1:MyBatis 是什么?它和 JDBC 有什么区别?
MyBatis 是一个半自动 ORM 框架,通过 XML 或注解配置 SQL 语句,将 Java 对象与数据库记录映射。JDBC 需要手动获取连接、创建 Statement、执行 SQL、处理结果集、关闭资源;MyBatis 封装了这些重复操作。
背诵口诀:半自动ORM写SQL,JDBC封装少写码,灵活度高好优化。
Q2:MyBatis 和 Hibernate 的区别?
| 对比项 | MyBatis | Hibernate |
|---|---|---|
| ORM类型 | 半自动 | 全自动 |
| SQL控制 | 手写SQL,灵活度高 | 自动生成,适合简单CRUD |
| 学习成本 | 较低 | 陡峭 |
| N+1问题 | 通过<association>/<collection>规避 |
常见,需额外优化 |
面试官追加 :Hibernate 的 N+1 查询问题怎么解决?(答:通过 <association> 和 <collection> 标签优化关联查询,或使用 @BatchSize)
Q3:MyBatis 的核心组件有哪些?
SqlSessionFactoryBuilder → SqlSessionFactory → SqlSession → Executor → MappedStatement → Mapper。
小结 :Configuration 存储所有配置,是 MyBatis 的"大脑"。
Q4:MyBatis 有哪些优点和缺点?
优点:SQL 与代码分离,灵活性高;支持动态 SQL;方便进行 SQL 优化。
缺点:需要手写 SQL;XML 配置较多;对数据库移植性有一定影响。
Q5:什么是 ORM?MyBatis 属于哪种 ORM?
ORM 全称 Object Relational Mapping(对象关系映射)。MyBatis 属于半自动 ORM,因为 SQL 需要开发者自己写。
三、核心SQL配置篇(8道真题)
Q6:#{} 和 ${} 的区别是什么?
| 对比项 | #{} |
${} |
|---|---|---|
| 底层处理 | 预编译占位符 → ? |
纯字符串替换 |
| SQL注入 | ✅ 防止 | ❌ 存在风险 |
| 适用场景 | 参数值传递 | 动态表名/列名 |
| 推荐度 | 优先使用 | 谨慎使用,需白名单 |
⚠️ 重点 :能用
#{}的地方绝不用${};表名/列名动态场景只能用${},但需白名单校验!
Q7:MyBatis 如何防止 SQL 注入?
核心是 #{} 预编译。SQL 模板先发到数据库预编译,参数值通过占位符独立传递,不被解析为 SQL 逻辑。
面试官追加 :MyBatis-Plus 的 LambdaQueryWrapper 如何防注入?(答:使用 Lambda 表达式封装字段名,避免字符串拼接)。
Q8:动态 SQL 有哪些标签?执行原理是什么?
包括 <if>、<choose>/<when>/<otherwise>、<where>、<set>、<foreach>、<trim>、<bind>。原理是根据 OGNL 表达式动态拼接 SQL。
Q9:<foreach> 标签的用法?
用于遍历集合,常见于批量插入或 IN 查询。
- ⚠️ 重点 :使用
ExecutorType.BATCH批量提交性能更佳,注意 MySQLmax_allowed_packet参数限制。
Q10:<bind> 标签的作用是什么?
从 OGNL 表达式中创建变量并绑定到上下文。常用于模糊查询拼接 %,避免在业务代码中硬编码。
Q11:实体类属性名和表字段名不一致怎么办?
方案一:resultMap 手动映射;方案二:开启驼峰命名转换;方案三:使用 @Results 注解。
Q12:resultType 和 resultMap 的区别?
resultType 要求列名与属性名一致,自动映射;resultMap 支持自定义映射,处理复杂关联。
Q13:MyBatis 分页如何实现?
方案一:SQL 手写 LIMIT;方案二:PageHelper 分页插件(底层拦截 Executor.query() 自动拼接 LIMIT)。
四、Mapper动态代理源码篇(5题)
Q14:Mapper 接口为什么不需要实现类?
MyBatis 使用 JDK 动态代理 。运行时生成 MapperProxy 代理对象,拦截接口方法,转而执行 MappedStatement 代表的 SQL。
背诵口诀:JDK代理生成Proxy,拦截方法找SQL。
Q15:Mapper 接口方法能重载吗?
不能 。MyBatis 使用 全限定名 + 方法名 作为唯一标识,重载会导致映射冲突。
Q16:MyBatis 执行 SQL 的完整流程?
(参见文章开头完整链路图)读取配置 → 创建 SqlSession → Mapper代理调用 → Executor执行 → 参数处理 → 结果映射 → 返回。
Q17:MyBatis 的 Executor 有哪些?
| 执行器 | 特点 | 适用场景 |
|---|---|---|
SimpleExecutor |
每次创建新Statement | 默认,通用 |
ReuseExecutor |
复用Statement | 减少创建开销 |
BatchExecutor |
批量执行 | 批量操作 |
CachingExecutor |
装饰器包装 | 开启二级缓存时 |
面试官追加 :开启二级缓存时用什么 Executor?(答:CachingExecutor,装饰器模式包装基础执行器)
Q18:MyBatis 插件(Interceptor)的原理?
拦截四大组件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)的方法,通过 InterceptorChain 形成责任链。
五、缓存机制篇(5题)
Q19:MyBatis 的一级缓存和二级缓存的区别?
| 对比项 | 一级缓存 | 二级缓存 |
|---|---|---|
| 作用范围 | SqlSession 级别 | Mapper(Namespace)级别 |
| 默认状态 | 默认开启 | 需手动配置 |
| 生命周期 | 会话关闭即销毁 | 跨SqlSession共享 |
| 实现方式 | 基于HashMap本地缓存 | 需配置<cache/> |
Q20:一级缓存失效的场景?
- 不同
SqlSession;2. 执行增删改操作;3. 手动clearCache();4. 事务提交/回滚;5.SqlSession关闭。
Q21:二级缓存如何配置?
Mapper XML 添加 <cache/> 标签,实体类实现 Serializable。
Q22:生产环境为什么建议禁用二级缓存?
⚠️ 重点 :在分布式微服务架构下,MyBatis 原生二级缓存基于本地内存,多节点部署极易导致数据不一致。生产环境标准做法:直接禁用,转而使用 Redis 等分布式缓存 !若需本地加速,推荐 Spring Cache + Caffeine。
Q23:二级缓存脏读问题如何解决?
多表关联更新不会自动清空其他表的缓存。解决:关联表缓存在同一 namespace;或使用 cache-ref;或多表查询禁用二级缓存。
💬 互动思考:你们项目是否踩过 MyBatis 二级缓存脏读问题?生产是如何解决的?评论区交流方案!
六、Spring整合篇(3题)
Q24:Spring 如何整合 MyBatis?
SqlSessionFactoryBean 创建工厂;@MapperScan 扫描接口注册为 MapperFactoryBean;SqlSessionTemplate 管理生命周期。
Q25:@MapperScan 注解的原理?
@Import(MapperScannerRegistrar.class),启动时扫描指定包,将 Mapper 接口注册为 MapperFactoryBean。
Q26:Spring 整合后 Mapper 注入的是什么对象?
注入的是 JDK 动态代理对象 (MapperProxy)。
七、MyBatis-Plus全考点(6题)
Q27:MyBatis-Plus 是什么?
MyBatis 的增强工具,只做增强不做改变。MP = MyBatis + 通用 CRUD + 条件构造 + 内置插件。
Q28:MyBatis-Plus 的核心组件有哪些?
BaseMapper;IService/ServiceImpl;Wrapper(条件构造器);Plugin(分页、乐观锁等);SqlInjector。
Q29:MP 如何在不修改 MyBatis 源码的前提下实现通用 CRUD?
依靠自动配置类
MybatisPlusAutoConfiguration扩展 MyBatis 原生机制,无侵入改造:
- Spring Boot 启动时自动注入
MybatisSqlSessionFactoryBean替代原生工厂 BeanMapperRegistry初始化阶段,通过AbstractSqlInjector动态生成insert、selectById等通用方法对应的MappedStatement- 将生成好的 SQL 映射注册至 MyBatis 全局
Configuration,完全复用 MyBatis 原生执行流程,不改动底层源码
Q30:MP 分页插件如何配置?如何优化 Count SQL 陷阱?
⚠️ 重点 :复杂 LEFT JOIN 查询中,自动生成的
COUNT(*)性能极差!最佳实践:关闭自动 count,手动编写轻量级 count SQL。
java
// MP分页Count优化标准代码
Page<User> page = new Page<>(pageNum, pageSize);
// 关闭自动count,复杂联查必开
page.setSearchCount(false);
// 仅查询主表统计,性能提升数倍
Long total = userMapper.selectCount(wrapper);
page.setTotal(total);
List<User> list = userMapper.selectPage(page, wrapper);
Q31:MyBatis-Plus 的逻辑删除原理?
@TableLogic 标记字段,删除变为 UPDATE,查询自动过滤。底层通过 LogicSqlInjector 改写 SQL。
Q32:MyBatis-Plus 的乐观锁如何实现?
实体类添加 @Version 字段,更新时检查版本号是否匹配,匹配则更新并 +1。
八、生产环境避坑汇总表
| 坑点 | 危害星级 | 正确做法 |
|---|---|---|
| SQL注入 | ⭐⭐⭐⭐⭐ | 能用 #{} 就别用 ${},动态表名需白名单校验 |
| 二级缓存分布式不一致 | ⭐⭐⭐⭐⭐ | 生产环境禁用 MyBatis 二级缓存,改用 Redis |
| MP分页COUNT陷阱 | ⭐⭐⭐⭐⭐ | 复杂查询手动设置 setSearchCount(false) + 手动count |
| 批量插入性能差 | ⭐⭐⭐⭐☆ | 使用 <foreach> 或 saveBatch,开启 rewriteBatchedStatements |
| Mapper重载 | ⭐⭐⭐☆☆ | 方法名保持唯一,避免映射冲突 |
| 逻辑删除未配置 | ⭐⭐⭐⭐☆ | 必须标注 @TableLogic,防止物理删除 |
九、课后面试真题练习
1. 分析题:某系统使用 MyBatis 二级缓存,关联表更新后其他查询读到旧数据,为什么?如何解决?
思路 :二级缓存基于 Mapper namespace,关联表更新不会清空其他表的缓存。解决:关联表缓存在同一 namespace,或使用
cache-ref,或在分布式环境下直接禁用改用 Redis。
2. 代码题 :使用 MyBatis-Plus 的 LambdaQueryWrapper 实现用户多条件分页查询(姓名模糊、年龄区间、状态精确),并处理复杂关联查询的 count 优化。
思路 :参考 Q30 代码示例,使用 Lambda 表达式封装条件,关闭自动 count 并手动设置
total。
3. 源码题:简述 MyBatis 插件(Interceptor)的拦截链构建过程。
思路 :
Configuration持有InterceptorChain存储全部插件;创建Executor/StatementHandler等四大对象时执行pluginAll();循环调用每个拦截器plugin方法生成多层代理,形成责任链。PageHelper、MP 分页插件均基于此机制。
📊 你的学习进度
- 当前:第90篇 / 共108篇 · 进阶篇:数据库与持久层框架(第83~90篇)
- ✅ 已完成:基础篇44篇 + 第91~96篇(Redis/MQ)+ 第83~90篇
- 📖 正在学:第90篇
- ⏳ 待学习:第97~108篇(微服务/物联网/AI/设计模式/面试压轴)
👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇
👉 下一篇文章预告
《第91篇:Redis核心数据结构(2026版)》
内容简介:Redis 五种核心数据结构(String、Hash、List、Set、ZSet)底层实现与适用场景,HyperLogLog、Bitmap 高级结构,生产环境避坑与面试高频题。
🎁 福利提醒:评论区留言"MyBatis面试"可领取《MyBatis 面试 32 题速答卡》PDF。
📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注 ,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!