MyBatis
MyBatis
MyBatis 面试八股文
1. 什么是 MyBatis?
MyBatis 是一款优秀的持久层框架,它 支持 SQL 语句的映射、动态 SQL 生成、缓存机制,简化了 JDBC 操作,避免了冗长的 SQL 代码。
MyBatis 不是全 ORM 框架,它只负责 SQL 语句的执行 & 结果映射,不像 Hibernate 那样完全自动化。
2. MyBatis 的优缺点
优点:
灵活性高:支持手写 SQL,适用于复杂查询。
性能优越:SQL 由开发者编写,避免了 Hibernate 级联查询带来的性能问题。
支持动态 SQL:可用
if、choose、where等标签动态拼接 SQL。缓存机制:提供 一级缓存 & 二级缓存,提高查询效率。
缺点:
SQL 需手写,SQL 语句需要自己管理,维护较复杂。
不支持自动建表,不像 JPA/Hibernate,无法自动创建数据库表。
学习成本较高,需要掌握 XML 语法和 SQL 语句。
3. MyBatis vs JPA(Hibernate)
| 对比项 | MyBatis | JPA(Hibernate) |
|---|---|---|
| SQL 控制 | 开发者手写 SQL,控制精准 | 自动生成 SQL,简化开发 |
| 查询性能 | SQL 可优化,性能高 | 可能生成复杂 SQL,性能受影响 |
| 学习成本 | 需要掌握 SQL 和 XML | 简单易学 |
| 缓存机制 | 一级缓存 & 二级缓存 | 内置缓存优化查询 |
| 维护成本 | SQL 维护量大 | 面向对象编程,易维护 |
| 事务管理 | 依赖 Spring 管理事务 | 内置事务管理 |
- 结论:
- 查询复杂、追求高性能,选 MyBatis;
- CRUD 简单、希望自动化,选 JPA(Hibernate)。
4. MyBatis 的核心组件
SqlSessionFactory:
负责创建 SqlSession 实例。
- 管理数据库连接、事务、缓存等。
SqlSession:
执行 SQL 语句,管理事务(增删改查)。
获取 Mapper(MyBatis 通过
Mapper接口操作数据库)。Mapper(映射器):
存放 SQL 语句的 XML 文件 或者 注解 SQL(
@Select、@Insert)。关联
Mapper.xml,实现 Java 方法 → SQL 语句的映射。配置文件(mybatis-config.xml):
管理全局配置(环境、数据源、别名、缓存策略等)。
映射文件(XXXMapper.xml):
定义 SQL 语句,提供 CRUD 操作。
5. MyBatis 工作流程
- 读取 MyBatis 配置文件(
mybatis-config.xml),创建SqlSessionFactory。 - 通过
SqlSessionFactory获取SqlSession,建立数据库连接。 - 调用 Mapper 执行 SQL,查询数据。
- 返回结果,MyBatis 解析 SQL 并封装为对象。
- 关闭
SqlSession,释放资源。
6. MyBatis 的一级缓存 & 二级缓存
一级缓存(默认开启):
作用范围:SqlSession 级别(同一个
SqlSession内部共享)。- 特点:
查询相同 SQL 不会再次访问数据库,直接从缓存取。
SqlSession.close()后缓存失效。二级缓存(手动开启):
作用范围:Mapper 级别(跨 SqlSession 共享)。
- 特点:
数据存储在
namespace级别的缓存中,不同SqlSession共享。需在
Mapper.xml添加<cache />手动开启。- 事务提交后才会缓存数据(不会立即写入缓存)。
7. MyBatis 如何处理懒加载(延迟加载)?
- 懒加载(Lazy Loading):只有 真正需要用到数据时才会加载,提高查询效率。
- 开启方式:
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>- 默认情况下,MyBatis 只会对关联对象(如
List<User>)进行懒加载,基本数据类型默认立即加载。
8. MyBatis 动态 SQL(灵活拼接 SQL 语句)
动态 SQL 作用:根据不同参数 动态生成 SQL,避免
if-else拼接字符串的方式,提高代码可读性。常用标签: | 标签 | 作用 | |---|---| |
<if>| 条件判断(if test="id != null") | |<choose>/<when>/<otherwise>| 类似switch-case语句 | |<where>| 自动去掉where后多余的AND| |<set>| 用于UPDATE语句,去掉SET后多余,| |<foreach>| 用于IN语句遍历数组 / List |示例:
<select id="getUserByCondition" resultType="User">
SELECT * FROM user
<where>
<if test="username != null">AND username = #{username}</if>
<if test="age != null">AND age = #{age}</if>
</where>
</select>9. MyBatis 中 #{}和 ${} 的区别
| 对比项 | #{}(安全) | ${}(不安全) |
|---|---|---|
| SQL 解析 | 占位符,使用 PreparedStatement 预编译 | 直接拼接 SQL,容易 SQL 注入 |
| SQL 语法 | WHERE id = #{id} | WHERE id = ${id} |
| 适用场景 | 安全插入参数 | 动态表名 / 列名 |
- 建议:大部分场景使用
#{},避免 SQL 注入!
MyBatis 中 resultMap 和 resultType
resultType(简单):- 适用于 查询结果和 Java 类字段名一致。
- 例如:
<select id="getUser" resultType="com.example.User">
SELECT id, name, age FROM user
</select>resultMap(复杂映射):- 用于字段名与数据库不匹配的情况(如
user_name -> userName)。 - 例如:
<resultMap id="UserResultMap" type="User">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
</resultMap>
<select id="getUser" resultMap="UserResultMap">
SELECT id, user_name FROM user
</select>MyBatis 进阶问题
MyBatis 如何实现动态数据源切换?
基于
AbstractRoutingDataSource结合 AOP 实现数据源动态切换。使用
ThreadLocal记录当前线程数据源信息。MyBatis 如何实现分页?
两种方式:
使用
RowBounds(内存分页,不推荐)。使用数据库
limit分页(推荐):
SELECT * FROM user LIMIT #{offset}, #{pageSize}- MyBatis 插件原理?
- 拦截
Executor、StatementHandler、ResultSetHandler等核心组件,实现 SQL 监控、日志打印、自动分页。
总结
- MyBatis 是 SQL 映射框架,支持手写 SQL & 高性能查询。
- 一级缓存 & 二级缓存 提高查询效率,但使用需谨慎。
- 动态 SQL 通过
<if>、<foreach>等标签提高灵活性。 - 插件机制 可用于拦截 SQL、分页、日志等功能。