一、基础概念
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 的工作原理(执行流程)是什么?
-
加载配置:SqlSessionFactoryBuilder 读取主配置文件和映射文件,解析后生成 Configuration 对象和 SqlSessionFactory。
-
创建会话:通过 SqlSessionFactory.openSession() 获取 SqlSession,该过程创建数据库连接并初始化执行器。
-
获取代理:调用 SqlSession.getMapper(Mapper接口.class) 获取 Mapper 接口的 JDK 动态代理对象。
-
执行 SQL:调用 Mapper 接口方法,代理对象根据方法名找到对应的 MappedStatement,交由 Executor 执行。Executor 通过 StatementHandler 创建 PreparedStatement,ParameterHandler 设置参数,然后执行 SQL,最后 ResultSetHandler 将结果集映射为 Java 对象返回。
-
关闭会话:操作完成后关闭 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. 当实体中的属性和表中的字段名不一致时,如何处理?
-
使用 ResultMap 显式定义映射 :在 XML 中定义
<resultMap>,明确列名与属性的对应关系。 -
开启驼峰命名自动映射 :在全局配置中设置
mapUnderscoreToCamelCase=true,自动将下划线字段名转换为驼峰属性名。 -
在 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:适合单表操作、快速开发、减少重复代码的场景。
最佳实践:混合使用,发挥各自优势。