知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
一、MyBatis SQL 执行流程图
sequenceDiagram
participant Client
participant MapperProxy
participant SqlSession
participant Executor
participant StatementHandler
participant ParameterHandler
participant ResultSetHandler
participant JDBC
Client->>MapperProxy: 调用Mapper接口方法
MapperProxy->>SqlSession: 获取SqlSession实例
SqlSession->>Executor: 委托执行
Executor->>Executor: 处理缓存(一级/二级)
Executor->>StatementHandler: 创建Statement
StatementHandler->>ParameterHandler: 设置参数
ParameterHandler->>JDBC: 填充PreparedStatement参数
JDBC->>JDBC: 执行SQL
JDBC->>ResultSetHandler: 返回ResultSet
ResultSetHandler->>Executor: 转换为Java对象
Executor->>SqlSession: 返回结果
SqlSession->>MapperProxy: 返回结果
MapperProxy->>Client: 返回结果
二、流程详解与源码分析
1. Mapper 接口方法调用(动态代理)
-
入口类 :
MapperProxy
(动态代理类) -
源码路径 :
org.apache.ibatis.binding.MapperProxy
-
核心逻辑 :
javapublic Object invoke(Object proxy, Method method, Object[] args) { // 1. 解析方法签名 MapperMethod mapperMethod = cachedMapperMethod(method); // 2. 调用 SqlSession 执行 SQL return mapperMethod.execute(sqlSession, args); }
2. SqlSession 委托执行
-
核心类 :
DefaultSqlSession
-
源码路径 :
org.apache.ibatis.session.defaults.DefaultSqlSession
-
关键方法 :
javapublic <E> List<E> selectList(String statement, Object parameter) { // 获取 MappedStatement(包含 SQL 定义) MappedStatement ms = configuration.getMappedStatement(statement); // 委托给 Executor 执行 return executor.query(ms, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER); }
3. Executor 处理缓存与执行
-
核心类 :
BaseExecutor
(抽象基类) -
源码路径 :
org.apache.ibatis.executor.BaseExecutor
-
关键逻辑 :
javapublic <E> List<E> query(MappedStatement ms, Object parameter, ...) { // 1. 生成缓存 Key CacheKey key = createCacheKey(ms, parameter, ...); // 2. 检查一级缓存 if (ms.isUseCache() && cache != null) { List<E> cachedList = (List<E>) cache.getObject(key); if (cachedList != null) return cachedList; } // 3. 未命中缓存,执行查询 return queryFromDatabase(ms, parameter, ...); } private <E> List<E> queryFromDatabase(...) { // 调用 doQuery()(子类实现) List<E> list = doQuery(ms, parameter, ...); // 写入一级缓存 if (ms.isUseCache()) cache.putObject(key, list); return list; }
4. StatementHandler 创建 Statement
-
核心类 :
PreparedStatementHandler
-
源码路径 :
org.apache.ibatis.executor.statement.PreparedStatementHandler
-
关键方法 :
javapublic <E> List<E> query(Statement statement, ResultHandler resultHandler) { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); // 处理结果集 return resultSetHandler.handleResultSets(ps); }
5. ParameterHandler 参数绑定
-
核心类 :
DefaultParameterHandler
-
源码路径 :
org.apache.ibatis.scripting.defaults.DefaultParameterHandler
-
关键逻辑 :
javapublic void setParameters(PreparedStatement ps) { // 遍历参数映射,逐个设置参数 for (ParameterMapping paramMapping : parameterMappings) { Object value = ...; // 从参数对象中提取值 TypeHandler typeHandler = paramMapping.getTypeHandler(); typeHandler.setParameter(ps, i + 1, value, paramMapping.getJdbcType()); } }
6. ResultSetHandler 结果映射
-
核心类 :
DefaultResultSetHandler
-
源码路径 :
org.apache.ibatis.executor.resultset.DefaultResultSetHandler
-
关键逻辑 :
javapublic List<Object> handleResultSets(Statement stmt) { // 遍历 ResultSet,逐行映射为 Java 对象 while (rsw != null && resultMapCount > resultSetIndex) { ResultMap resultMap = resultMaps.get(resultSetIndex); Object rowValue = getRowValue(rsw, resultMap); addToResultMap(rowValue); } return resultList; }
三、核心类图与交互
plaintext
MapperProxy → SqlSession → Executor → StatementHandler → JDBC
↑ ↓
Cache ResultSetHandler
四、关键设计模式
- 动态代理模式 :
MapperProxy
拦截接口方法调用。 - 模板方法模式 :
BaseExecutor
定义执行流程,子类实现具体逻辑。 - 装饰器模式 :
CachingExecutor
包装普通 Executor 添加缓存功能。 - 责任链模式 :
InterceptorChain
管理插件拦截逻辑。
五、调试技巧与实战示例
1. 断点设置
- MapperProxy.invoke():观察方法调用如何转换为 SQL 操作。
- PreparedStatementHandler.query():跟踪 JDBC 执行过程。
- DefaultResultSetHandler.handleResultSets():分析结果集映射逻辑。
2. 日志输出
在 mybatis-config.xml
中开启 Debug 日志:
xml
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
3. 实战示例:跟踪 SELECT 查询
java
// Mapper 接口
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(int id);
}
// 调用代码
User user = sqlSession.getMapper(UserMapper.class).getUserById(1);
- 执行路径 :
MapperProxy
→DefaultSqlSession.selectOne()
→CachingExecutor.query()
→PreparedStatementHandler.query()
→DefaultResultSetHandler.handleResultSets()
.
六、高级特性扩展
1. 插件拦截 SQL 执行
-
拦截点 :
Executor
、StatementHandler
、ParameterHandler
、ResultSetHandler
。 -
示例插件 :统计 SQL 执行时间。
java@Intercepts(@Signature(type = Executor.class, method = "query", args = {...})) public class SqlTimeInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { long start = System.currentTimeMillis(); Object result = invocation.proceed(); System.out.println("SQL 耗时: " + (System.currentTimeMillis() - start) + "ms"); return result; } }
2. 自定义类型处理器
-
核心接口 :
TypeHandler<T>
-
示例 :处理枚举类型存储为字符串。
javapublic class EnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> { @Override public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) { ps.setString(i, parameter.name()); } @Override public E getResult(ResultSet rs, String columnName) { String value = rs.getString(columnName); return Enum.valueOf(type, value); } }
七、总结
通过以上流程分析,可以清晰看到 MyBatis 如何将 Mapper 接口的方法调用转化为 JDBC 操作。核心在于:
- 动态代理:将接口调用映射到 SQL 执行。
- 执行器链 :通过
Executor
实现缓存、事务管理。 - 结果映射 :灵活地将
ResultSet
转为 Java 对象。