一次 Mapper 方法调用最终仍是 JDBC:准备 SQL、绑定参数、发送给数据库、读取结果。MyBatis 的价值是把这些重复步骤组成稳定管线,而不是绕过 JDBC。
Table of contents
Open Table of contents
一次查询的主线
MapperProxy
→ MappedStatement
→ Executor
→ StatementHandler
→ ParameterHandler
→ JDBC PreparedStatement
→ ResultSetHandler
→ Java 对象
Executor 负责缓存、事务相关协调和执行策略;StatementHandler 创建并执行 JDBC Statement;ParameterHandler 把 Java 参数绑定到 ?;ResultSetHandler 把结果集转换为对象。
OrderMapper.java下载开启了下划线转驼峰映射,所以 user_id 能进入 userId。生产查询建议明确列名,不写 SELECT *,这样 Schema 变化不会悄悄改变映射和网络负载。
一级缓存为什么只执行一次 SQL
运行:
mvn -Dtest=DataArchitectureLabTest#firstLevelCacheAvoidsSecondStatementPreparation test
同一个 SqlSession 用相同语句、参数和分页条件查询两次,第二次命中本地缓存。一级缓存默认作用域是会话;执行更新、提交、回滚或关闭会话会清理相关缓存。它不能替代 Redis,也不能让多个请求共享数据。
二级缓存以 Mapper 命名空间为边界,可跨会话,但一致性、序列化和失效更复杂。除非已证明数据库读取是瓶颈并设计好失效策略,否则先不用它。
结果映射的两种方式
简单表可以自动映射。关联对象、一对多集合或不同命名需要 resultMap 明确声明。嵌套查询容易产生 N+1:查 100 个订单后,再为每个订单查一次用户,总计 101 条 SQL。嵌套结果则用一次 Join 返回扁平行,再组装对象,但要注意重复数据量。
排查顺序
遇到“字段为 null”时,依次检查 SQL 列名、别名、Java 属性名、构造器、TypeHandler 和自动映射设置;遇到“SQL 次数过多”,先记录语句 ID、耗时和调用次数,再判断是缓存未命中还是 N+1。
下一步
继续阅读06-03 MyBatis 插件、批处理与性能陷阱,用插件观察 SQL,并把批处理的提交语义说清楚。