MyBatis源码解析:从SqlSession到JDBC的完整执行链路

你是否曾好奇:当你调用MyBatis的selectOne()方法时,你的SQL语句究竟经历了怎样的旅程才最终到达数据库?本文将深入MyBatis内核,揭示从SqlSession到JDBC的完整执行链路,让你彻底掌握MyBatis的核心工作原理。

一、执行链路全景图

在深入源码前,先看整体执行流程:

SqlSession -> Executor -> StatementHandler -> ParameterHandler -> JDBC Statement -> ResultSetHandler

二、SqlSession:入口门户

SqlSession是MyBatis的核心接口,所有数据库操作都从这里开始。以DefaultSqlSession为例:

// DefaultSqlSession.java
public <E> List<E> selectList(String statement, Object parameter) {
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
}


关键点:

  1. 通过statement从Configuration获取MappedStatement
  2. 委托给Executor执行查询

三、Executor:执行引擎

Executor是真正的执行者,采用典型的命令模式。以SimpleExecutor为例:

// SimpleExecutor.java
public <E> List<E> query(MappedStatement ms, Object parameter, 
                         RowBounds rowBounds, ResultHandler resultHandler) {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

执行过程:

  1. 获取BoundSql(包含最终要执行的SQL和参数映射)
  2. 创建缓存Key(如果启用二级缓存)
  3. 执行doQuery()方法

四、StatementHandler:SQL语句处理器

Executor最终会调用StatementHandler:

// SimpleExecutor.doQuery()
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, 
                                  rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);

PreparedStatementHandler核心处理流程:

// SimpleExecutor.doQuery()
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, 
                                  rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);

PreparedStatementHandler核心处理流程:

// PreparedStatementHandler.java
public <E> List<E> query(Statement statement, ResultHandler resultHandler) {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
}

五、ParameterHandler:参数处理专家

参数处理发生在StatementHandler初始化时:

// DefaultParameterHandler.java
public void setParameters(PreparedStatement ps) {
    for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        Object value;
        // 复杂参数处理逻辑...
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
    }
}

六、JDBC交互:最后的桥梁

MyBatis最终通过标准的JDBC API与数据库交互:

// PreparedStatementHandler.execute()
public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return ps.getUpdateCount();
}

七、ResultSetHandler:结果集转换大师

结果集处理是最复杂的部分:

// DefaultResultSetHandler.java
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    final List<Object> multipleResults = new ArrayList<>();
    ResultSet rs = stmt.getResultSet();
    while (rs != null) {
        // 处理单结果集
        Object result = getSingleResult(rs);
        multipleResults.add(result);
        // 处理多结果集
        if (stmt.getMoreResults()) {
            rs = stmt.getResultSet();
        } else {
            rs = null;
        }
    }
    return collapseSingleResultList(multipleResults);
}

八、设计模式应用

MyBatis执行链路中巧妙运用了多种设计模式:

  1. 门面模式:SqlSession作为统一入口
  2. 责任链模式:插件拦截器实现
  3. 模板方法模式:BaseExecutor定义执行骨架
  4. 装饰器模式:CachingExecutor包装普通Executor

九、性能优化关键点

  1. Statement重用:ReuseExecutor会缓存PreparedStatement
  2. 批量处理:BatchExecutor优化批量操作
  3. 延迟加载:通过Proxy实现关联对象的延迟加载
  4. 缓存机制:一级缓存(Session级别)和二级缓存(Mapper级别)

十、插件扩展原理

MyBatis通过动态代理实现插件机制:

// Plugin.java
public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
        return Proxy.newProxyInstance(
            type.getClassLoader(),
            interfaces,
            new Plugin(target, interceptor, signatureMap));
    }
    return target;
}

结语

通过本文的源码级分析,你应该已经掌握了MyBatis从SqlSession到JDBC的完整执行链路。理解这些底层原理不仅能帮助你在遇到问题时快速定位,更能让你在复杂业务场景下做出更合理的设计决策。下次当你使用MyBatis时,不妨在脑海中回想这个执行流程,相信你会对它有全新的认识。

原文链接:,转发请注明来源!