🔥 MyBatis面试10题(2026版)

更新时间:2026年5月 | 来源:真实面经汇总 | 每道题3句话答案+代码片段

1. MyBatis中 #{} 和 ${} 的区别是什么?

标准答案:
🔹 #{}是预编译占位符,会被替换为?,通过PreparedStatement安全设置参数,防止SQL注入
🔹 ${}是字符串直接拼接,存在SQL注入风险,只用于表名、字段名等动态替换场景。
🔹 面试加分:"生产环境99%用#{},${}只用于ORDER BY动态字段排序,且必须配合白名单校验。"
-- #{}
SELECT * FROM user WHERE id = #{id}  -- SQL: WHERE id = ?

-- ${}(危险)
SELECT * FROM user ORDER BY ${column}  -- 仅此场景,加白名单

2. MyBatis一二级缓存是什么?有什么区别?

标准答案:
🔹 一级缓存:SqlSession级别,默认开启,同一个SqlSession内的相同查询只查一次数据库。
🔹 二级缓存:Mapper级别(跨SqlSession),需要手动配置<cache>开启,多个SqlSession共享。
🔹 关键点:二级缓存在分布式环境下会数据不一致——A服务更新了数据,B服务的缓存不会感知,所以生产环境基本不用,统一用Redis替代
<mapper namespace="UserMapper">
    <cache eviction="LRU" flushInterval="60000" size="512"/>
</mapper>

3. MyBatis分页怎么实现?原理是什么?

标准答案:
🔹 使用PageHelper分页插件,在查询前调用PageHelper.startPage(page, size)
🔹 原理:PageHelper是MyBatis拦截器,在SQL执行前拦截,自动拼接LIMIT语句。
🔹 面试加分:"底层用ThreadLocal绑定分页参数,执行后自动清理。MyBatis-Plus内置分页插件更简洁。"
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();  // SQL自动加 LIMIT 0,10

4. MyBatis延迟加载原理?

标准答案:
🔹 延迟加载(懒加载):关联对象在被真正使用时才去数据库查询,不是查询主对象时就一并查出。
🔹 原理:MyBatis使用CGLIB/Javassist动态代理,为关联属性创建代理对象,首次调用getter时触发查询。
🔹 配置:lazyLoadingEnabled=true + aggressiveLazyLoading=false
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

5. MyBatis-Plus和原生MyBatis有什么区别?

标准答案:
🔹 MP是MyBatis的增强工具,内置通用CRUD(BaseMapper.selectById()),不用写XML。
🔹 提供条件构造器Wrapper、分页插件、乐观锁、逻辑删除开箱即用。
🔹 面试加分:"MP适合快速开发,但复杂多表查询仍用原生XML,两者可以共存。"
// MyBatis-Plus:一行代码查全部
List<User> users = userMapper.selectList(
    new LambdaQueryWrapper<User>().eq(User::getStatus, 1)
);

6. MyBatis动态SQL有哪些标签?

标准答案:
🔹 <if>:条件判断;<where>:自动去掉第一个AND/OR。
🔹 <foreach>:遍历集合生成IN查询;<trim>:自定义截断。
🔹 重点:<choose> <when> <otherwise>实现多分支条件,类似switch-case。

7. MyBatis中resultType和resultMap有什么区别?

标准答案:
🔹 resultType:自动映射,要求数据库字段名和Java属性名完全一致
🔹 resultMap:手动定义映射关系,处理字段名不一致、嵌套对象、一对多/多对一场景。
🔹 面试加分:"生产环境统一用resultMap——可读性更好,方便后续维护和关联查询。"

8. MyBatis中如何实现一对一、一对多关联查询?

标准答案:
🔹 一对一<association>标签,如User关联Dept。
🔹 一对多<collection>标签,如Dept包含List<User>。
🔹 N+1问题:查询N个主对象时执行N次子查询。解决方案:用JOIN语句一次性查出,或配置懒加载。

9. MyBatis插件(拦截器)原理?

标准答案:
🔹 MyBatis插件通过JDK动态代理拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler四个核心对象。
🔹 使用@Intercepts注解声明拦截点,@Signature指定拦截的方法。
🔹 典型应用:PageHelper分页插件、慢SQL监控、数据脱敏。
@Intercepts(@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class}))
public class SlowSqlInterceptor implements Interceptor { ... }

10. MyBatis批量插入怎么优化?

标准答案:
🔹 方法一:<foreach>拼VALUES多行——一条SQL插多条,但数据量大时SQL过长可能导致OOM
🔹 方法二(推荐):MyBatis-Plus的saveBatch或JDBC的executeBatch——每1000条一批。
🔹 面试加分:"还应关闭二级缓存、事务控制、适当调大数据库buffer_pool。"