跳到主要内容

执行流程

问题

MyBatis 的整体架构是怎样的?一条 SQL 从发起到返回结果经历了哪些步骤?

答案

MyBatis 架构分层

SQL 执行完整流程

核心组件详解

组件职责
SqlSessionFactory创建 SqlSession 的工厂,全局唯一
SqlSession和数据库交互的会话,非线程安全
ExecutorSQL 执行器,有 Simple/Reuse/Batch 三种
MappedStatement封装一条 SQL 的完整信息(SQL、参数映射、结果映射)
StatementHandler创建和操作 JDBC Statement
ParameterHandler将 Java 参数设置到 PreparedStatement
ResultSetHandler将 JDBC ResultSet 映射为 Java 对象
TypeHandlerJava 类型 ↔ JDBC 类型的双向转换

三种 Executor

Executor特点
SimpleExecutor每次执行创建新的 Statement(默认)
ReuseExecutor相同 SQL 复用 Statement
BatchExecutor批量执行 update(addBatch/executeBatch)
Executor 选择
// 默认 SimpleExecutor
SqlSession session = sqlSessionFactory.openSession();

// 指定 Executor 类型
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);

SqlSession 工作原理

SqlSession 使用(原始方式)
try (SqlSession session = sqlSessionFactory.openSession()) {
// 方式1:直接使用 SqlSession
User user = session.selectOne("com.example.mapper.UserMapper.getById", 1L);

// 方式2:通过 Mapper 接口(推荐)
UserMapper mapper = session.getMapper(UserMapper.class);
User user2 = mapper.getById(1L);

session.commit();
}
SqlSession 线程安全

SqlSession 是非线程安全的,不能作为成员变量共享。在 Spring 中,SqlSessionTemplate 通过动态代理为每次调用创建新的 SqlSession,保证线程安全。


常见面试问题

Q1: MyBatis 的执行流程?

答案

  1. 应用通过 Mapper 接口调用方法
  2. Mapper 代理将方法调用转化为 SqlSession.selectOne/insert/update/delete
  3. SqlSession 委托给 Executor 执行
  4. Executor 先查缓存(一级 → 二级),命中则直接返回
  5. 未命中则创建 StatementHandler
  6. ParameterHandler 通过 TypeHandler 设置 SQL 参数
  7. 执行 JDBC 操作
  8. ResultSetHandler 通过 TypeHandler 将结果映射为 Java 对象
  9. 结果放入一级缓存后返回

Q2: #{}${} 的区别?

答案

  • #{}预编译参数,使用 PreparedStatement? 占位符,防止 SQL 注入
  • ${}字符串拼接,直接将值拼入 SQL,有 SQL 注入风险

#{} 用于传值(where 条件、insert 值),${} 仅用于动态表名、列名等无法预编译的场景。

Q3: Mapper 接口没有实现类,是怎么工作的?

答案

MyBatis 通过 JDK 动态代理 实现。SqlSession.getMapper(UserMapper.class) 返回的是一个代理对象,调用接口方法时,代理拦截调用,根据方法的全限定名(类名.方法名)找到对应的 MappedStatement,最终委托给 SqlSession 执行 SQL。

Q4: MyBatis 中 XML 和注解方式的区别?

答案

方式优点缺点
XML适合复杂 SQL,便于维护文件多,需要额外配置
注解简单直接,无需额外文件复杂 SQL 不方便

实际项目中常混用:简单 CRUD 用注解,复杂查询用 XML。

相关链接