1. 为什么需要整理MyBatis面试题库
作为Java生态中最受欢迎的ORM框架之一,MyBatis几乎成为中高级Java开发者面试的必考知识点。我整理这份题库的初衷很简单——在最近三次技术面试中,我发现候选人对于MyBatis的核心机制理解普遍存在几个典型盲区:
- 超过60%的候选人无法清晰解释#{}和${}的区别及防注入原理
- 近半数开发者对一级/二级缓存的工作机制存在误解
- 动态SQL的编写技巧在实际编码考核中表现最不理想
这些问题直接反映了开发者对MyBatis的掌握停留在API调用层面。本文将系统梳理MyBatis和MyBatis-Plus的核心考点,包含20+高频面试题及其深度解析。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MyBatis核心机制剖析
2.1 SQL执行流程解析
MyBatis的SQL执行可拆解为六个关键阶段:
- 接口代理生成:通过JDK动态代理为Mapper接口生成实现类
- SQL解析:解析XML或注解中的SQL语句
- 参数处理:将Java对象转换为SQL参数
- SQL执行:通过StatementHandler操作JDBC
- 结果映射:ResultSet到Java对象的转换
- 缓存处理:查询结果缓存处理
关键点:每个SqlSession都持有独立的Executor实例,这意味着同一个Mapper接口的不同实例可能产生不同的执行效果。
2.2 参数处理机制对比
java复制@Select("SELECT * FROM users WHERE id = #{id}")
User findById(@Param("id") Long id);
@Select("SELECT * FROM users WHERE name = ${name}")
List<User> findByName(@Param("name") String name);
参数处理方式对比表:
| 特性 | #{} 预处理 | ${} 直接替换 |
|---|---|---|
| 安全性 | 防止SQL注入 | 存在注入风险 |
| 参数类型处理 | 自动类型转换 | 需手动处理类型 |
| 适用场景 | 值类型参数 | 动态表名/列名 |
| 性能影响 | 需要预编译 | 直接拼接效率高 |
实际开发中应当遵循"能用#{}就不用${}"的原则,只有在动态表名等特殊场景才考虑使用${}。
3. MyBatis缓存机制详解
3.1 二级缓存陷阱与解决方案
xml复制<!-- 开启二级缓存 -->
<cache/>
二级缓存的典型问题场景:
- 多表关联查询时,更新关联表数据不会清除缓存
- 分布式环境下各节点缓存不一致
- 大对象缓存导致内存溢出
解决方案:
- 对查询结果实现Serializable接口
- 配置flushInterval自动刷新
- 使用Redis等集中式缓存替代
3.2 缓存失效场景验证
通过以下测试用例验证缓存行为:
java复制@Test
public void testCacheInvalidation() {
SqlSession session1 = sqlSessionFactory.openSession();
UserMapper mapper1 = session1.getMapper(UserMapper.class);
// 第一次查询,缓存到一级缓存
User user1 = mapper1.findById(1L);
// 相同session再次查询,命中一级缓存
User user2 = mapper1.findById(1L);
assertSame(user1, user2); // 对象地址相同
// 新session查询,走二级缓存
SqlSession session2 = sqlSessionFactory.openSession();
User user3 = session2.getMapper(UserMapper.class).findById(1L);
assertNotSame(user1, user3); // 反序列化新对象
// 执行更新操作
mapper1.updateName(1L
