MyBatis体系结构与工作原理 上篇

课程内容

昨天内容

ORM 演化

为什么要学源码?

提高技术能力 解决工作中的问题

环境搭建




体系结构

接口层面向开发者

mybatis-》核心处理层

基础支持层 核心

MyBatis 执行 SQL 时,Mapper 代理调用 SqlSession,SqlSession 委托 Executor,Executor 经缓存与插件处理后交给 StatementHandler 构建并执行 JDBC 语句,ParameterHandler 完成参数映射,ResultSetHandler 将结果映射为对象并返回。

今天重点 -核心处理层

源码结构

源码学习

测试代码

test2

复制代码
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(in);
SqlSessionFactory

SqlSessionFactory 作用

SqlSessionFactory 是 MyBatis 的"会话工厂",负责根据全局配置创建 SqlSession,并统一管理 MyBatis 的运行环境。

build方法

全局配置解析 映射文件解析 XMLConfigBuilder

复制代码
​
复制代码
///别名注册
​
public Configuration() {
​
 // 为类型注册别名
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
​
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
}
复制代码
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

XML 解析时如何通过 alias 实例化组件

复制代码
 public class TypeAliasRegistry类 {
  public TypeAliasRegistry() {
    registerAlias("string", String.class);
​
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
  private final Map<String, Class<?>> typeAliases = new HashMap<>();
​
public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    typeAliases.put(key, value);
  }
复制代码
private final Map<String, Class<?>> typeAliases = new HashMap<>();

默认值的初始化

反射组件不知道哪个适合启用了

复制代码
private void parseConfiguration(XNode root) {

继续分析

复制代码
return build(parser.parse());
复制代码
 build(Configuration config)

(parser.parse() 解析了Configuration

root包含了整个配置文件

复制代码
​
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      // ===== MyBatis 全局配置文件(mybatis-config.xml)解析主流程 =====
​
// 1. 解析 <properties>:加载外部属性文件/变量(用于占位符 ${} 替换)  <properties resource="db.properties"/>
      propertiesElement(root.evalNode("properties"));
​
// 2. 解析 <settings>:读取框架核心开关配置(如缓存、懒加载、驼峰映射等)
      Properties settings = settingsAsProperties(root.evalNode("settings"));
​
// 2.1 根据 settings 加载自定义 VFS 实现(用于资源扫描,如 Jar/OSGi 等)
      loadCustomVfs(settings);
​
// 2.2 根据 settings 设置日志实现(SLF4J、LOG4J、STDOUT 等)
      loadCustomLogImpl(settings);
​
// 3. 解析 <typeAliases>:注册类型别名(简化 XML 中类名配置)
      typeAliasesElement(root.evalNode("typeAliases"));
​
// 4. 解析 <plugins>:加载 MyBatis 插件体系(Interceptor 拦截 Executor/StatementHandler 等)
      pluginElement(root.evalNode("plugins"));
​
// 5. 解析 <objectFactory>:指定对象创建工厂(控制结果对象实例化方式)
      objectFactoryElement(root.evalNode("objectFactory"));
​
// 6. 解析 <objectWrapperFactory>:对象包装工厂(用于增强对象属性访问,如 Map/Collection)
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
​
// 7. 解析 <reflectorFactory>:反射缓存工厂(提升反射性能,管理 Reflector)
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
​
// 8. 应用 <settings> 到 Configuration:将 settings 值真正赋给全局配置对象(默认值也在此补全)
      settingsElement(settings);
​
// 注意:settings 必须在 objectFactory/objectWrapperFactory 初始化后设置(历史 issue #631)
​
// 9. 解析 <environments>:构建运行环境(事务管理器 + 数据源 DataSource)
//    → 最终决定 Connection 从哪里来、事务如何控制
      environmentsElement(root.evalNode("environments"));
​
// 10. 解析 <databaseIdProvider>:数据库厂商识别(用于多数据库 SQL 区分,如 mysql/oracle)
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
​
// 11. 解析 <typeHandlers>:注册类型转换器(Java 类型 ↔ JDBC 类型映射)
      typeHandlerElement(root.evalNode("typeHandlers"));
​
// 12. 解析 <mappers>:加载 Mapper 映射器(XML/注解)
//     → 生成 MappedStatement,建立 statementId → SQL 的映射关系
      mapperElement(root.evalNode("mappers"));
​
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

信息保存

复制代码
// 12. 解析 <mappers>:加载 Mapper 映射器(XML/注解)
//     → 生成 MappedStatement,建立 statementId → SQL 的映射关系
      mapperElement(root.evalNode("mappers"));
复制代码
​
复制代码
XMLStatementBuilder
Mapper XML 中每一个 <select> 节点都会对应一个 XMLStatementBuilder 解析器,它基于 XNode 节点内容,在 MapperBuilderAssistant 辅助下生成 MappedStatement,并注册到全局 Configuration 中,供后续 SqlSession 执行时查找调用。
对象/字段 类型 是否"每个SQL节点一个" 核心作用 类比理解
statementParser XMLStatementBuilder ✅ 是 专门解析某一个 <select>/<insert> 节点,最终生成 MappedStatement 单条SQL的"编译器"
builderAssistant MapperBuilderAssistant ❌ 一个Mapper文件共享 提供命名空间、辅助注册 statement/resultMap 等 Mapper构建助手
context XNode ✅ 是 当前 XML 节点对象(保存SQL标签内容和属性) DOM节点包装
requiredDatabaseId String ❌ 可选 多数据库适配用,判断是否加载该SQL 数据库过滤器
configuration Configuration ❌ 全局唯一 MyBatis运行期核心容器,存放所有Mapper、SQL、插件等 全局注册中心
typeAliasRegistry TypeAliasRegistry ❌ 全局唯一 类型别名管理(XML中短名称→Class) 类名字典
typeHandlerRegistry TypeHandlerRegistry ❌ 全局唯一 Java类型↔JDBC类型转换器注册表 类型转换中心

一个标签的转化 增删改查的标签

复制代码
statementParser.parseStatementNode();

xmlstatemxml

复制代码
 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
复制代码
​
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);
​
​
复制代码
 // 最关键的一步,在 Configuration 添加了 MappedStatement >>
    configuration.addMappedStatement(statement);
    return statement;
复制代码
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
      .conflictMessageProducer((savedValue, targetValue) ->
          ". please check " + savedValue.getResource() + " and " + targetValue.getResource());

statement 对应的crud标签

复制代码
public class DefaultSqlSessionFactory implements SqlSessionFactory {

时序图

会话对象

DefaultSqlSession

它是应用层与 MyBatis 执行器(Executor)之间的桥梁,负责SQL调用的统一入口与事务管理。

复制代码
  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
​

configuration.getDefaultExecutorType()

复制代码
  public ExecutorType getDefaultExecutorType() {
    return defaultExecutorType;
  }
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
 * @author Clinton Begin
 */
public enum ExecutorType {
  SIMPLE, REUSE, BATCH
}
​

执行器作用

ExecutorType 执行器机制 是否复用 Statement 是否批量执行 典型使用场景 优点 注意点
SIMPLE 每次执行创建新的 Statement 否 否 默认 CRUD、普通查询 行为最直观、安全 性能一般,频繁创建 Statement
REUSE 复用已创建的 Statement 是 否 同一 SQL 多次执行 减少 Statement 创建开销 只复用 Statement,不合并 SQL
BATCH 批量缓存 SQL,统一提交 是(批量) 是 批量 insert / update 大幅提升批量写性能 需手动 flushStatements()

ExecutorType 决定 MyBatis SQL 的执行模型 ,defaultExecutorType 决定 全局默认行为。

复制代码
public final class Environment {
  private final String id;
  private final TransactionFactory transactionFactory;
  private final DataSource dataSource;
    
 

Environment 类在 MyBatis 中是非常核心的配置对象,它主要用来封装一个运行环境下 数据库连接和事务相关的配置。

复制代码
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        ///获取数据库信息
      final Environment environment = configuration.getEnvironment();
      // 获取事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 创建事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根据事务工厂和默认的执行器类型,创建执行器 >>
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

final Executor executor = configuration.newExecutor(tx, execType); 执行sql语句的执行器

Rule

复制代码
 // 缓存 Statement
private final Map<String, Statement> statementMap = new HashMap<>();
​
public ReuseExecutor(Configuration configuration, Transaction transaction) {
  super(configuration, transaction);
}
复制代码
executor = new ReuseExecutor(this, transaction);// 缓存
​

不同的策略对应不同的选择,例如缓存,就可以直接从缓存中拿

复制代码
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }
select代理创建

sql语句的执行

getMapper

复制代码
  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
复制代码
public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
复制代码
  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 1:类加载器:2:被代理类实现的接口、3:实现了 InvocationHandler 的触发管理类
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
复制代码
public class MapperProxy<T> implements InvocationHandler, Serializable {
​
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // toString hashCode equals getClass等方法,无需走到执行SQL的流程
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
        // 提升获取 mapperMethod 的效率,到 MapperMethodInvoker(内部接口) 的 invoke
        // 普通方法会走到 PlainMethodInvoker(内部类) 的 invoke
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
代理执行

代理对象的MapperProxy

复制代码
public class MapperProxy<T> implements InvocationHandler, Serializable {
​
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // toString hashCode equals getClass等方法,无需走到执行SQL的流程
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
        // 提升获取 mapperMethod 的效率,到 MapperMethodInvoker(内部接口) 的 invoke
        // 普通方法会走到 PlainMethodInvoker(内部类) 的 invoke
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
复制代码
/// 返回的MapperMethodInvoker 这个实例
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
复制代码
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
  return mapperMethod.execute(sqlSession, args);
}
复制代码
  private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;
​
    public PlainMethodInvoker(MapperMethod mapperMethod) {
      super();
      this.mapperMethod = mapperMethod;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      // SQL执行的真正起点
      return mapperMethod.execute(sqlSession, args);
    }
  }
sql执行
复制代码
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    //拿取指之前初始化的MappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 如果 cacheEnabled = true(默认),Executor会被 CachingExecutor装饰
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
复制代码
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    // cache 对象是在哪里创建的?  XMLMapperBuilder类 xmlconfigurationElement()
    // 由 <cache> 标签决定
    if (cache != null) {
      // flushCache="true" 清空一级二级缓存 >>
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        // 获取二级缓存
        // 缓存通过 TransactionalCacheManager、TransactionalCache 管理
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // 写入二级缓存
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    // 走到 SimpleExecutor | ReuseExecutor | BatchExecutor
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

增强

复制代码
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
​
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 植入插件逻辑(返回代理对象)
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
​
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // StatementType 是怎么来的? 增删改查标签中的 statementType="PREPARED",默认值 PREPARED
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        // 创建 StatementHandler 的时候做了什么? >>
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
​
  }
复制代码
stmt = prepareStatement(handler, ms.getStatementLog());
复制代码
//获取链接
protected Connection getConnection(Log statementLog) throws SQLException {
  Connection connection = transaction.getConnection();
  if (statementLog.isDebugEnabled()) {
    return ConnectionLogger.newInstance(connection, statementLog, queryStack);
  } else {
    return connection;
  }
}
复制代码
//获取Statement
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
层次 对象 关系
JDBC Statement / PreparedStatement 一条 SQL 对应一个 Statement
MyBatis SqlSession 可以执行多条 SQL,一个 SqlSession 可以生成多个 Statement
MyBatis Executor 每条 SQL 会通过 Executor 调用 StatementHandler 生成 Statement
返回处理
复制代码
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }
复制代码
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
​
    // 设置当前错误上下文:正在处理结果集,并绑定当前 MappedStatement 的 id
    ErrorContext.instance()
            .activity("handling results")
            .object(mappedStatement.getId());
​
    // 用于存放所有结果集解析后的结果(可能是多个 ResultSet)
    final List<Object> multipleResults = new ArrayList<>();
​
    // 当前处理到第几个 ResultSet(从 0 开始)
    int resultSetCount = 0;
​
    // 获取第一个 ResultSet,并包装成 ResultSetWrapper(便于统一处理元数据)
    ResultSetWrapper rsw = getFirstResultSet(stmt);
​
    // 从 MappedStatement 中获取所有配置的 ResultMap
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
​
    // ResultMap 的数量
    int resultMapCount = resultMaps.size();
​
    // 校验 ResultSet 与 ResultMap 的数量是否匹配(防止配置错误)
    validateResultMapsCount(rsw, resultMapCount);
​
    /**
     * 第一阶段:
     * 处理“主结果集”
     * - 一个 ResultSet 对应一个 ResultMap
     * - 常见于普通查询或多结果集返回
     */
    while (rsw != null && resultMapCount > resultSetCount) {
​
        // 根据当前索引获取对应的 ResultMap
        ResultMap resultMap = resultMaps.get(resultSetCount);
​
        // 处理当前 ResultSet:
        // - rsw:当前结果集
        // - resultMap:映射规则
        // - multipleResults:将结果加入到该集合中
        // - null:此时不是嵌套结果映射
        handleResultSet(rsw, resultMap, multipleResults, null);
​
        // 获取下一个 ResultSet(Statement 可能返回多个)
        rsw = getNextResultSet(stmt);
​
        // 清理当前 ResultSet 处理过程中产生的临时状态
        cleanUpAfterHandlingResultSet();
​
        // 结果集索引递增
        resultSetCount++;
    }
​
    /**
     * 第二阶段:
     * 处理“嵌套结果集”(多 ResultSet 关联)
     * - 通常用于存储过程
     * - 通过 resultSets 属性指定 ResultSet 名称
     * - 与父结果集进行关联映射
     */
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
​
        // 继续遍历剩余的 ResultSet
        while (rsw != null && resultSetCount < resultSets.length) {
​
            // 根据 resultSets 中的名称,找到对应的父 ResultMapping
            ResultMapping parentMapping =
                    nextResultMaps.get(resultSets[resultSetCount]);
​
            if (parentMapping != null) {
​
                // 获取嵌套 ResultMap 的 id
                String nestedResultMapId =
                        parentMapping.getNestedResultMapId();
​
                // 从 Configuration 中获取嵌套 ResultMap
                ResultMap resultMap =
                        configuration.getResultMap(nestedResultMapId);
​
                // 处理嵌套 ResultSet:
                // - multipleResults 为 null(不作为独立结果返回)
                // - parentMapping 用于将结果挂接到父对象
                handleResultSet(rsw, resultMap, null, parentMapping);
            }
​
            // 获取下一个 ResultSet
            rsw = getNextResultSet(stmt);
​
            // 清理临时状态
            cleanUpAfterHandlingResultSet();
​
            // 索引递增
            resultSetCount++;
        }
    }
​
    /**
     * 如果最终只有一个结果集:
     * - 返回该结果本身(而不是 List<List<>>)
     * 如果有多个结果集:
     * - 返回 List<Object>
     */
    return collapseSingleResultList(multipleResults);
}
​
复制代码
/**
 * 处理单个 ResultSet,并根据是否为嵌套映射决定结果的去向
 *
 * @param rsw             ResultSet 的包装对象,包含结果集及其元数据
 * @param resultMap       当前 ResultSet 对应的 ResultMap 映射规则
 * @param multipleResults 用于存放“主结果集”的结果列表(嵌套结果时为 null)
 * @param parentMapping   父级 ResultMapping(不为 null 表示这是嵌套结果集)
 */
private void handleResultSet(ResultSetWrapper rsw,
                             ResultMap resultMap,
                             List<Object> multipleResults,
                             ResultMapping parentMapping) throws SQLException {
  try {
​
    /**
     * 场景一:嵌套结果集(多 ResultSet 关联)
     * - parentMapping 不为 null
     * - 当前 ResultSet 的数据需要“挂接”到父结果对象上
     * - 不作为独立查询结果返回
     */
    if (parentMapping != null) {
​
      // 处理行数据:
      // - resultHandler 为 null(不需要收集成独立结果)
      // - RowBounds.DEFAULT:不进行分页
      // - parentMapping:用于父子对象关联
      handleRowValues(
              rsw,
              resultMap,
              null,
              RowBounds.DEFAULT,
              parentMapping
      );
​
    } else {
​
      /**
       * 场景二:普通结果集(非嵌套)
       */
​
      // 如果用户没有自定义 ResultHandler
      if (resultHandler == null) {
​
        // 使用默认的结果处理器
        DefaultResultHandler defaultResultHandler =
                new DefaultResultHandler(objectFactory);
​
        // 处理行数据:
        // - 将每一行映射成对象
        // - 由 DefaultResultHandler 收集到内部 List 中
        handleRowValues(
                rsw,
                resultMap,
                defaultResultHandler,
                rowBounds,
                null
        );
​
        // 将当前 ResultSet 的结果(一个 List)加入 multipleResults
        multipleResults.add(
                defaultResultHandler.getResultList()
        );
​
      } else {
​
        /**
         * 场景三:用户自定义 ResultHandler
         * - MyBatis 不再收集结果
         * - 由用户自行处理每一行数据(如流式处理、大数据量)
         */
        handleRowValues(
                rsw,
                resultMap,
                resultHandler,
                rowBounds,
                null
        );
      }
    }
​
  } finally {
​
    /**
     * 无论是否发生异常,都必须关闭 ResultSet
     * issue #228:
     * - 修复 ResultSet 未关闭导致的数据库资源泄露问题
     */
    closeResultSet(rsw.getResultSet());
  }
}
​

核心工具类

核心流程

相关推荐
Joy T9 小时前
Agent 确定性上下文设计:从 Prompt 参数注入到能力分层
java·开发语言·微服务·架构·prompt·agent context
m0_5873830012 小时前
广州 24 小时自助健身房解决方案技术实现与实战指南
java·spring·小程序·架构·需求分析
·云扬·12 小时前
Prompt 工程与安全:从 System 设计到注入防御
java·人工智能·安全·ai·prompt
代码不停13 小时前
自动化测试(java)
java·开发语言·selenium·自动化
鬼手点金15 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
paopaokaka_luck16 小时前
滑雪场管理系统(AI智能助手流式对话,ECharts 运营数据分析、教练、雪具与场地一体化预约,钱包支付退款、订单核销与服务评价)
java·人工智能·spring boot·网络协议·数据分析·echarts
Wang's Blog16 小时前
Java 项目实战: 交友项目 - Dubbo 的内部结构与一次调用的完整链路
java·dubbo
Wang's Blog16 小时前
Java 项目实战: 交友项目 - Dubbo 入门引导
java·dubbo·交友
蜗牛互联网17 小时前
Python Agent工具调用的白名单、参数校验与审批门禁
java·服务器·前端·人工智能·后端·python
SL_staff17 小时前
当规则引擎开始‘懂业务’:JVS-Rules金融风控四大原生能力拆解
java·python·全栈