📌 专栏连载:《2026全新 | Java 100 天进阶之路:从零基础到上岗就业,108篇完整学习地图》
⬅️ 上一篇:第89篇:MySQL面试压轴题 |
➡️ 下一篇:第91篇:Redis核心数据结构
文章目录
一、MyBatis SQL完整执行流程链路图
💡 重点:面试官问执行流程时,直接画出这张图,秒杀80%只会背文字的竞争者!
二、基础概念篇(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。
// 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!
👉 点击关注我,更新后第一时间收到推送!
&spm=1001.2101.3001.5002&articleId=162104537&d=1&t=3&u=045e6a069e6d4a27acef2d693388e829)
329

被折叠的 条评论
为什么被折叠?



