以下是通过 时序图 和 文字说明 详细描述的 MyBatis 执行一条 SELECT
语句的完整流程,包括缓存查询、数据库访问和结果返回的各个环节:
时序图(Sequence Diagram)
详细执行流程解析
1. 客户端发起查询请求
- 客户端调用
SqlSession
的查询方法(如selectOne
),传入 SQL 语句的标识(如selectUserById
)和参数(如id=1
)。
2. 二级缓存检查(CachingExecutor)
CachingExecutor
作为装饰器,优先检查 二级缓存(对应Mapper
级别的缓存):
-
- 若命中,直接返回缓存结果(流程结束)。
- 若未命中,继续委托给底层
Executor
(如SimpleExecutor
或BatchExecutor
)处理。
3. 一级缓存检查(BaseExecutor)
BaseExecutor
负责处理 一级缓存(当前SqlSession
的缓存):
-
- 若命中,返回缓存结果。
- 若未命中,继续执行数据库查询。
4. 数据库查询
- 4.1 创建 PreparedStatement
StatementHandler
通过Connection
创建PreparedStatement
对象。 - 4.2 参数绑定
ParameterHandler
将参数(如id=1
)绑定到 SQL 中的占位符(?
)。 - 4.3 执行 SQL
通过 JDBC 驱动执行 SQL,向数据库发送查询请求。 - 4.4 结果映射
ResultSetHandler
将返回的ResultSet
转换为 Java 对象(如User
对象)。
5. 缓存更新
- 一级缓存更新:
查询结果立即存入当前SqlSession
的一级缓存(localCache
)。 - 二级缓存更新:
若SqlSession
执行了commit()
或close()
,且二级缓存已开启(<cache/>
配置),则结果会同步到二级缓存。
6. 结果返回
- 最终结果通过
SqlSession
返回给客户端。
关键组件作用
组件 | 职责 |
| 用户操作 MyBatis 的入口,封装了会话状态和数据库操作。 |
| 执行器,负责缓存查询和 SQL 执行(分为 |
| 装饰器模式实现,添加二级缓存逻辑。 |
| 处理 SQL 语句(创建 |
| 绑定 SQL 参数。 |
| 处理结果集(映射为 Java 对象)。 |
| 实际执行数据库操作。 |
缓存与数据库访问的优先级
- 二级缓存(跨会话共享) → 2. 一级缓存(当前会话) → 3. 数据库
-
- 若二级缓存未命中,但一级缓存命中,结果会直接返回,不会更新二级缓存。
- 二级缓存的更新需等待会话提交或关闭。
示例场景
场景 1:首次查询(无缓存)
// 1. 客户端发起查询
User user = sqlSession.selectOne("selectUserById", 1);
// 执行流程:
// 二级缓存未命中 → 一级缓存未命中 → 查询数据库 → 结果存入一级缓存
// 提交后同步到二级缓存
sqlSession.commit();
场景 2:同一会话重复查询
// 第一次查询(查数据库)
User user1 = sqlSession.selectOne("selectUserById", 1);
// 第二次查询(命中一级缓存)
User user2 = sqlSession.selectOne("selectUserById", 1);
场景 3:跨会话查询(二级缓存生效)
// Session1 查询并提交
SqlSession session1 = sqlSessionFactory.openSession();
User user1 = session1.selectOne("selectUserById", 1);
session1.commit(); // 同步到二级缓存
session1.close();
// Session2 查询(命中二级缓存)
SqlSession session2 = sqlSessionFactory.openSession();
User user2 = session2.selectOne("selectUserById", 1);
注意事项
- 缓存一致性:
-
- 更新操作(INSERT/UPDATE/DELETE)会清空一级缓存(当前会话)和二级缓存(全局)。
- 事务提交:
-
- 未提交的会话中,二级缓存不会更新。
- 序列化要求:
-
- 二级缓存的对象需实现
Serializable
接口。
- 二级缓存的对象需实现
- 性能权衡:
-
- 高频写入场景下,建议关闭二级缓存,避免频繁缓存失效。
总结
MyBatis 的 SELECT
执行流程通过 二级缓存 → 一级缓存 → 数据库 的优先级策略,最大化利用缓存提升性能。理解其内部组件协作(如 Executor
、StatementHandler
)和缓存更新机制,有助于优化数据访问层设计,平衡性能与数据一致性。