MyBatis & MyBatis-Plus 面试题整理

一、基础概念

1. 什么是 MyBatis?它的核心优势是什么?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 消除了几乎所有的 JDBC 代码和参数的手动设置以及结果集的检索,通过简单的 XML 或注解配置,将接口和 Java 的 POJO 映射成数据库中的记录。

核心优势

  • 半自动化 ORM:允许开发者手动编写 SQL 语句,灵活可控,特别适合复杂查询和需要精细控制 SQL 性能的场景。

  • 低侵入性:不强制实体类继承任何父类或实现接口,实体类就是普通 POJO。

  • 易于整合:与 Spring、Spring Boot 等主流框架无缝集成。

  • 缓存机制:内置一级缓存(SqlSession 级别)和二级缓存(Mapper 级别),提升查询效率。

  • 动态 SQL:通过 XML 标签根据条件动态拼接 SQL,避免在 Java 代码中繁琐的字符串拼接。

  • 结果集映射:提供灵活的结果集映射机制,支持自动映射和自定义复杂映射。

2. MyBatis 与 Hibernate 的区别?适用场景是什么?

对比维度 MyBatis Hibernate
ORM 类型 半自动化:SQL 由开发者手动编写,框架只负责参数和结果映射。 全自动化:SQL 由框架自动生成,开发者操作对象即可。
SQL 控制度 高,便于优化复杂 SQL,利用数据库特性。 低,难以干预 SQL 生成,优化复杂查询较困难。
学习成本 低,只需熟悉 SQL 和简单映射规则。 高,需掌握 HQL、缓存策略、映射配置等。
开发效率 一般,需手动编写 SQL,但对复杂查询更直接。 高,简单 CRUD 几乎不用写 SQL。
性能优化 容易,可直接优化 SQL。 困难,需深入理解框架机制。
适用场景 复杂 SQL、性能要求高、互联网项目。 简单 CRUD 为主、快速开发、企业级应用。

3. 什么是 MyBatis-Plus?它的核心优势是什么?

MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,旨在简化开发、提高效率。它提供了通用 Mapper、条件构造器、内置插件等能力。

核心优势

  • 无侵入:完全兼容 MyBatis,可平滑迁移。

  • 通用 CRUD:内置 BaseMapper 和 IService 接口,提供 17+ 通用 CRUD 方法,无需编写 SQL。

  • 条件构造器:提供 Wrapper 系列(如 QueryWrapper、LambdaQueryWrapper)支持链式编程动态拼接 SQL 条件。

  • 内置插件:自带分页、乐观锁、逻辑删除、性能分析等插件,开箱即用。

  • 主键策略:支持自增、UUID、雪花算法等多种主键生成策略。

  • Lambda 支持:通过 Lambda 表达式引用字段,避免硬编码。

4. MyBatis-Plus 与 MyBatis 的区别?

MyBatis-Plus 是 MyBatis 的超集,在保留 MyBatis 所有功能的基础上进行增强:

维度 MyBatis MyBatis-Plus
基本 CRUD 需手动编写 SQL 和映射 继承 BaseMapper 即可获得通用 CRUD 方法
条件查询 需在 XML 中编写动态 SQL 使用 Wrapper 链式调用动态拼接条件
分页 需手动实现或集成插件 内置分页插件,通过 Page 对象完成
乐观锁 需手动实现 内置乐观锁插件,通过 @Version 注解
逻辑删除 需手动处理 内置逻辑删除插件,通过 @TableLogic 注解
复杂 SQL 擅长,可手写任意复杂 SQL 适合单表操作,复杂查询可混合原生 MyBatis 实现

二、核心原理

5. MyBatis 的核心组件有哪些?各自作用是什么?

  • SqlSessionFactory:工厂类,用于创建 SqlSession,由 SqlSessionFactoryBuilder 根据配置文件构建。

  • SqlSession:数据库会话,提供 CRUD 操作 API,线程不安全,使用后需关闭。

  • Mapper 接口:开发者自定义的 DAO 接口,MyBatis 通过动态代理生成实现类。

  • Mapper.xml:SQL 映射文件,存放 SQL 语句、参数映射、结果集映射。

  • Executor:执行器,负责 SQL 解析、参数处理、结果映射,有 Simple、Reuse、Batch 三种实现。

  • StatementHandler:处理 JDBC Statement,负责参数设置、SQL 执行、结果集封装。

  • ResultSetHandler:将 JDBC ResultSet 映射为 Java 对象。

  • ParameterHandler:负责将参数设置到 PreparedStatement 中。

  • TypeHandler:处理 Java 类型与 JDBC 类型之间的相互转换。

6. MyBatis 的工作原理(执行流程)是什么?

  1. 加载配置:SqlSessionFactoryBuilder 读取主配置文件和映射文件,解析后生成 Configuration 对象和 SqlSessionFactory。

  2. 创建会话:通过 SqlSessionFactory.openSession() 获取 SqlSession,该过程创建数据库连接并初始化执行器。

  3. 获取代理:调用 SqlSession.getMapper(Mapper接口.class) 获取 Mapper 接口的 JDK 动态代理对象。

  4. 执行 SQL:调用 Mapper 接口方法,代理对象根据方法名找到对应的 MappedStatement,交由 Executor 执行。Executor 通过 StatementHandler 创建 PreparedStatement,ParameterHandler 设置参数,然后执行 SQL,最后 ResultSetHandler 将结果集映射为 Java 对象返回。

  5. 关闭会话:操作完成后关闭 SqlSession,释放数据库连接并清空一级缓存。

7. Mapper 接口工作原理是什么?方法能重载吗?

工作原理

  • Mapper 接口的全限定名对应映射文件中的 namespace 值。

  • 接口中的方法名对应 namespace 下某个 SQL 标签的 id 值。

  • 调用接口方法时,MyBatis 通过"全限定名+方法名"作为唯一标识找到对应的 MappedStatement,然后执行 SQL。

  • MyBatis 使用 JDK 动态代理为 Mapper 接口生成代理对象,代理对象内部调用 SqlSession 的相应方法。

方法能否重载?

不能重载。因为 MyBatis 定位 SQL 的唯一键是"namespace+方法名",如果接口中存在同名方法(即使参数不同),会导致无法唯一确定要执行的 SQL 语句。

8. MyBatis 都有哪些 Executor 执行器?它们之间的区别是什么?

  • SimpleExecutor:默认执行器,每执行一次 update 或 select 就创建一个 Statement 对象,用完立即关闭。

  • ReuseExecutor:可复用执行器,内部缓存 Statement 对象,以 SQL 语句为 key,执行相同 SQL 时复用 Statement,减少创建开销。

  • BatchExecutor:批处理执行器,用于执行批量更新操作(不支持 select),将所有 SQL 添加到批处理中,一次性提交到数据库,提高批量操作性能。

可在配置文件中通过 defaultExecutorType 设置默认执行器,或在创建 SqlSession 时动态指定。

9. MyBatis-Plus 的核心组件有哪些?

  • BaseMapper:通用 Mapper 接口,提供 17+ 个基础的 CRUD 方法,如 insert、selectById、updateById、deleteById、selectList 等。

  • IService / ServiceImpl:通用 Service 层接口和实现类,封装了业务级常用方法(如 saveBatch、page、getById 等),并注入对应 Mapper。

  • Wrapper:条件构造器抽象类,子类包括 QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper,用于动态拼接 SQL 条件。

  • MybatisPlusInterceptor:插件拦截器,管理多个内置插件(分页、乐观锁、逻辑删除等)并按顺序执行。

  • GlobalConfig:全局配置类,用于配置主键策略、逻辑删除字段名等全局行为。

  • SqlInjector:SQL 注入器,负责将 BaseMapper 中的通用方法对应的 SQL 动态注入到 MyBatis 的 MappedStatement 中。

10. IService 与 BaseMapper 的区别?什么时候用 IService?

  • BaseMapper 属于 DAO 层,提供基础的 CRUD 方法,方法命名偏向 MyBatis 风格(如 insert、selectById)。

  • IService 属于 Service 层,提供业务级方法(如 saveBatch、page、saveOrUpdate),方法命名更贴近业务语义,且内置批量操作的分批处理逻辑。

使用场景

  • 当项目有明确的 Service 层时,推荐使用 IService,它提供了更丰富的业务方法和更好的事务管理。

  • 简单 Demo 或没有 Service 层的项目可直接使用 BaseMapper。

三、参数与结果映射

11. #{} 和 ${} 的区别?

  • #{} :预编译参数占位符,MyBatis 会将其替换为 ?,生成 PreparedStatement,然后通过 set 方法安全设置参数,能有效防止 SQL 注入。适用于传递参数值。

  • ${}:字符串直接拼接,MyBatis 直接将参数值拼接到 SQL 语句中,存在 SQL 注入风险。适用于动态表名、动态列名等无法使用占位符的场景。

结论:优先使用 #{},仅在必要时(如动态表名)使用 ${},并严格过滤输入。

12. 什么是 ResultMap?为什么要用它?

ResultMap 是 MyBatis 中用于定义数据库查询结果集与 Java 对象属性映射关系的标签。

使用场景

  • 数据库字段名与实体类属性名不一致时,通过 ResultMap 明确映射关系。

  • 复杂查询(多表关联、嵌套查询、集合属性)需要将结果组装到对象的嵌套属性中。

  • 避免在 SQL 中大量使用别名,提高可维护性。

13. 当实体中的属性和表中的字段名不一致时,如何处理?

  1. 使用 ResultMap 显式定义映射 :在 XML 中定义 <resultMap>,明确列名与属性的对应关系。

  2. 开启驼峰命名自动映射 :在全局配置中设置 mapUnderscoreToCamelCase=true,自动将下划线字段名转换为驼峰属性名。

  3. 在 SQL 中使用别名 :通过 AS 将字段别名设置为属性名,如 select user_name as userName from user

14. MyBatis-Plus 中最常用的注解是什么?它们的作用是什么?

  • @TableName:标注实体类对应的数据库表名。

  • @TableId:标注主键字段,可指定主键生成策略(如自增、雪花算法)。

  • @TableField:标注非主键字段,可指定数据库列名、字段填充策略、是否为数据库字段等。

  • @Version:标注乐观锁版本号字段。

  • @TableLogic:标注逻辑删除字段。

15. MyBatis-Plus 支持哪些常见的主键策略?

  • AUTO:数据库自增 ID。

  • ASSIGN_ID:分配 ID(雪花算法),生成 64 位长整型 ID。

  • ASSIGN_UUID:分配 UUID,生成 32 位字符串。

  • INPUT:用户输入 ID,需手动设置。

  • NONE:无状态,未设置主键类型。

可在实体类通过 @TableId(type = IdType.XXX) 指定,也可在全局配置中统一设置。

四、动态 SQL

16. MyBatis 动态 SQL 常用标签有哪些?

  • <if>:条件判断,满足条件则拼接 SQL 片段。

  • <where>:用于 WHERE 子句,自动去除第一个条件前的 AND 或 OR。

  • <set>:用于 UPDATE 语句的 SET 子句,自动去除最后一个逗号。

  • <foreach>:遍历集合,常用于 IN 条件或批量插入。

  • <choose><when><otherwise>:类似 switch-case,从多个条件中选择一个执行。

  • <trim>:更通用的格式化标签,可自定义去除或添加前缀/后缀。

  • <bind>:创建变量并绑定到上下文,常用于模糊查询拼接 % 符号。

17. MyBatis-Plus 的 Wrapper 有哪些常用实现类?区别是什么?

  • QueryWrapper:普通查询条件构造器,通过字符串指定字段名(硬编码)。

  • LambdaQueryWrapper:Lambda 风格的查询条件构造器,通过 Lambda 表达式引用实体属性,避免硬编码。

  • UpdateWrapper:更新条件构造器,用于动态拼接 UPDATE 的 SET 和 WHERE 条件。

  • LambdaUpdateWrapper:Lambda 风格的更新条件构造器,结合 Lambda 的安全性和 UpdateWrapper 的灵活性。

推荐优先使用 LambdaQueryWrapper 和 LambdaUpdateWrapper。

五、缓存机制

18. MyBatis 的一级缓存和二级缓存有什么区别?

维度 一级缓存 二级缓存
作用范围 单个 SqlSession 内有效 同一个 Mapper(跨 SqlSession)
默认状态 开启,无法关闭 关闭,需手动开启
存储介质 内存(HashMap) 内存或第三方缓存(如 Redis)
失效场景 SqlSession 关闭/提交/回滚;执行增删改操作 执行增删改操作;缓存过期;手动清除

执行流程:查询时先查二级缓存,再查一级缓存,最后查数据库;会话关闭时一级缓存数据可转入二级缓存。

六、分页与插件

19. MyBatis 分页方式?

  • RowBounds:逻辑分页,一次性查询所有数据,在内存中截取指定范围,性能较差。

  • 分页插件:物理分页,通过插件机制拦截 SQL 并改写为带物理分页语句(如 LIMIT),性能高,需引入插件(如 PageHelper、MyBatis-Plus 分页插件)。

20. MyBatis-Plus 内置了哪些插件?

  • 分页插件

  • 乐观锁插件

  • 逻辑删除插件

  • 性能分析插件

  • 多租户插件

  • 动态表名插件

  • SQL 性能规范插件

七、高级特性

21. MyBatis 是否支持延迟加载?实现原理是什么?

支持情况 :支持,但仅限 association(一对一)和 collection(一对多)的关联查询,需开启 lazyLoadingEnabled=true

实现原理

  • MyBatis 使用 CGLIB 创建目标对象的代理对象。

  • 当访问关联对象的属性时,代理对象拦截方法调用,执行预先保存的关联查询 SQL,将结果加载并设置到真实对象中。

  • 之后再次访问直接使用已加载的数据,不再执行 SQL。

22. MyBatis 能执行一对一、一对多的关联查询吗?实现方式有哪些?

。实现方式有两种:

  • 嵌套查询:先查询主对象,再通过单独的 SQL 查询关联对象,可配合延迟加载优化。

  • 嵌套结果 :使用 JOIN 查询一次性将主对象和关联对象的所有字段查出,通过 <resultMap> 中的 <association><collection> 配置映射关系,MyBatis 根据 <id> 标签去重复并组装对象。

23. MyBatis-Plus 与 MyBatis 的主要区别总结?

维度 MyBatis MyBatis-Plus
基本 CRUD 需手动编写 SQL 继承 BaseMapper 即可获得通用方法
条件查询 需在 XML 中编写动态 SQL 使用 Wrapper 链式调用
分页 需手动实现或集成插件 内置分页插件,通过 Page 对象完成
乐观锁 需手动实现 内置乐观锁插件,通过 @Version 注解
逻辑删除 需手动处理 内置逻辑删除插件,通过 @TableLogic 注解
复杂 SQL 擅长,可手写任意 SQL 适合单表操作,复杂查询可混合原生 MyBatis

适用场景

  • MyBatis:适合复杂 SQL、多表联查、需要精细控制 SQL 的场景。

  • MyBatis-Plus:适合单表操作、快速开发、减少重复代码的场景。

最佳实践:混合使用,发挥各自优势。

相关推荐
梦里1米82 小时前
大模型的使用和Prompt-Tuning学习笔记
笔记·学习·prompt
xiaoye37082 小时前
CentOS 7 搭建Maven私服
java·maven
北极糊的狐2 小时前
MySQL常见报错分析及解决方案总结(42)---ERROR 1142 (42000): SELECT command denied
java·mysql·adb·myeclipse
云边散步2 小时前
godot2D游戏教程系列二(11)
笔记·学习·游戏·游戏开发
升鲜宝供应链及收银系统源代码服务2 小时前
升鲜宝生鲜配送供应链管理系统生产加工子模块的详细表设计说明
java·大数据·前端·数据库·bootstrap·供应链系统·生鲜配送
6+h2 小时前
【Spring】深度剖析AOP
java·后端·spring
试试勇气2 小时前
Linux学习笔记(十六)--进程信号
linux·笔记·学习
云边散步2 小时前
godot2D游戏教程系列二(8)
笔记·学习·游戏·游戏开发