MyBatis XMLMapperBuilder 是如何将 SQL 语句解析成可执行的对象? 如何将结果映射规则解析成对应的处理器?

发布于:2025-03-20 ⋅ 阅读:(16) ⋅ 点赞:(0)

1. XMLMapperBuilder 如何将 SQL 语句解析成可执行对象 (MappedStatement):

XMLMapperBuilder 解析 <select>, <insert>, <update>, <delete> 等 SQL 语句元素时,并不仅仅是简单地读取 SQL 文本,而是要将 SQL 语句和相关的配置信息 封装成 MappedStatement 对象MappedStatement 对象才是 MyBatis 运行时真正可执行的 SQL 对象。 这个过程主要涉及以下几个关键步骤:

  • 1.1. 解析 SQL 语句文本 (SqlSource):

    XMLMapperBuilder 会读取 <select>, <insert>, <update>, <delete> 元素体内的 SQL 语句文本。 SQL 语句文本可能包含:

    • 静态 SQL: 普通的 SQL 语句,不包含任何动态 SQL 标签或占位符。
    • 动态 SQL: 包含 MyBatis 的动态 SQL 标签 (例如 <if>, <choose>, <foreach>, <where>, <set>, <trim>, ${}, #{} 等)。
    • 参数占位符: #{}${} 占位符,用于在运行时动态地替换参数值。

    XMLMapperBuilder 会根据 SQL 语句文本的类型,创建不同的 SqlSource 对象来表示 SQL 语句的来源和处理方式。 SqlSource 接口是 MyBatis 中表示 SQL 语句来源的抽象接口,它有以下几种实现类:

    • RawSqlSource: 用于表示 静态 SQL。 对于静态 SQL,SQL 语句文本在解析时就已经确定,运行时无需动态构建。
    • DynamicSqlSource: 用于表示 动态 SQL。 对于动态 SQL,SQL 语句文本在运行时需要根据参数值进行动态构建。 DynamicSqlSource 会负责解析和处理动态 SQL 标签,并生成最终的可执行 SQL 语句。
    • ProviderSqlSource: 用于表示 基于 Provider 类的 SQL。 SQL 语句不是直接写在 XML 文件中,而是通过一个 Java Provider 类动态生成。

    XMLMapperBuilder 会根据 SQL 语句是否包含动态 SQL 标签来判断创建 RawSqlSource 还是 DynamicSqlSource。 如果 SQL 语句中使用了动态 SQL 标签,则创建 DynamicSqlSource,否则创建 RawSqlSource。 如果是基于 Provider 类的 SQL,则创建 ProviderSqlSource

    示例代码 (简化版,展示 XMLMapperBuilder 如何创建 SqlSource):

    // 假设 XMLMapperBuilder 解析 <select id="getUserById"> 元素
    XNode selectNode = ...; // 代表 <select> 元素的 XNode 对象
    String sqlText = selectNode.getStringBody(); // 获取 <select> 元素体内的 SQL 语句文本
    
    SqlSource sqlSource;
    if (sqlTextContainsDynamicSqlTags(sqlText)) { // 检查 SQL 文本是否包含动态 SQL 标签 (简化判断逻辑)
        sqlSource = new DynamicSqlSource(configuration, selectNode); // 创建 DynamicSqlSource
    } else {
        sqlSource = new RawSqlSource(configuration, selectText); // 创建 RawSqlSource
    }
    
  • 1.2. 解析参数映射 (ParameterMap 和 内联参数映射):

    XMLMapperBuilder 会解析 SQL 语句中的参数映射配置。 MyBatis 支持两种参数映射方式:

    • parameterMap 属性 (已过时,不推荐使用): 通过 <select>, <insert>, <update>, <delete> 元素的 parameterMap 属性引用外部定义的 <parameterMap> 元素。 XMLMapperBuilder 会解析 <parameterMap> 元素及其子元素 <parameter>,构建 ParameterMap 对象,并将其关联到 MappedStatementparameterMap 方式已经过时,不推荐使用。
    • 内联参数映射 (#{}${}): 在 SQL 语句文本中直接使用 #{}${} 占位符进行参数映射。 这是 现代 MyBatis 开发中推荐使用的参数映射方式XMLMapperBuilder 会解析 SQL 语句文本中的 #{}${} 占位符,并提取占位符中的参数属性名、jdbcType、typeHandler 等信息。

    XMLMapperBuilder 会将解析得到的参数映射信息 存储到 MappedStatement 对象中,以便在运行时进行参数绑定。

  • 1.3. 解析结果映射 (ResultMap 和 resultType):

    XMLMapperBuilder 会解析 <select> 元素的结果映射配置,用于将查询结果集映射到 Java 对象。 MyBatis 支持两种结果映射方式:

    • resultMap 属性: 通过 <select> 元素的 resultMap 属性引用外部定义的 <resultMap> 元素。 XMLMapperBuilder 会解析 <resultMap> 元素及其子元素 (<id>, <result>, <association>, <collection>, <discriminator>), 构建 ResultMap 对象,并将其关联到 MappedStatementresultMap 方式适用于复杂的结果集映射场景 (例如关联查询、集合属性、多态映射等)。
    • resultType 属性: 通过 <select> 元素的 resultType 属性直接指定结果类型。 resultType 方式适用于简单的结果集映射场景 (例如单表查询,结果类型是基本类型或 POJO)。 MyBatis 会自动进行简单的属性映射。

    XMLMapperBuilder 会根据 <select> 元素配置的 resultMapresultType 属性, 创建 ResultMap 对象 (如果使用 resultMap) 或记录 resultType,并将结果映射信息存储到 MappedStatement 对象中,以便在运行时进行结果集映射。

  • 1.4. 构建 MappedStatement 对象:

    XMLMapperBuilder 在完成 SQL 语句文本 (SqlSource)、参数映射和结果映射的解析后,会将这些信息,以及 <select>, <insert>, <update>, <delete> 元素上的其他属性 (例如 statementType, timeout, fetchSize, cache 等), 整合到一个 MappedStatement 对象中

    MappedStatement 对象包含了执行一个 SQL 操作所需的所有信息,是 MyBatis 运行时执行 SQL 的核心对象。

  • 1.5. 注册 MappedStatementConfiguration:

    XMLMapperBuilder 会将构建好的 MappedStatement 对象 注册到 Configuration 对象的 mappedStatements 属性 (一个 StrictMap<MappedStatement>) 中。 注册时,会使用 Mapper 接口的全限定名 + SQL 语句的 id 属性作为 MappedStatement 的唯一 ID (mappedStatementId)。

2. XMLMapperBuilder 如何将结果映射规则解析成对应的处理器 (ResultMap 和 ResultHandler):

XMLMapperBuilder 在解析 <resultMap> 元素时,会构建 ResultMap 对象。 ResultMap 对象本身 不是处理器 (Handler),而是 结果映射规则的定义。 它描述了如何将查询结果集中的列映射到 Java 对象的属性。

真正的结果集映射处理器是 ResultHandler 接口的实现类。 ResultHandler 接口负责 逐行处理查询结果集,并将每一行数据按照 ResultMapresultType 定义的映射规则,映射到 Java 对象。

XMLMapperBuilder 在解析结果映射规则时,主要完成以下工作,为后续的结果集映射处理做准备:

  • 2.1. 构建 ResultMap 对象 (如果定义了 <resultMap>):

    XMLMapperBuilder 解析 <resultMap> 元素时,会根据 <resultMap> 元素的配置信息 (包括 id, type, extends, autoMapping, <constructor>, <id>, <result>, <association>, <collection>, <discriminator>), 构建一个 ResultMap 对象ResultMap 对象会存储结果映射的所有规则,例如:

    • id: resultMap 的唯一 ID。
    • type: 结果映射的目标 Java 类型。
    • resultMappings: 一个 List<ResultMapping>, 存储了所有的属性映射规则 (<id>, <result>, <association>, <collection>, <discriminator> 对应的 ResultMapping 对象)。
    • constructorResultMappings, idResultMappings, propertyResultMappings, associationResultMappings, collectionResultMappings, discriminatorResultMappings: 不同类型的 ResultMapping 列表,方便按类型查找。
    • autoMapping: 是否开启自动映射。
    • extendsResultMap: 继承的 ResultMap 的 ID。
    • discriminator: 鉴别器 (Discriminator)。

    ResultMap 对象本身并不执行映射操作,它只是结果映射规则的描述。

  • 2.2. 将 ResultMap 对象注册到 Configuration:

    XMLMapperBuilder 会将构建好的 ResultMap 对象 注册到 Configuration 对象的 resultMapRegistry 属性 (一个 ResultMapRegistry 对象) 中,使用 resultMapid 作为 key。

  • 2.3. 运行时,MyBatis 使用 ResultHandlerResultMap 进行结果集映射:

    在 MyBatis 运行时执行 SqlSession.selectList(), SqlSession.selectOne() 等查询方法时,MyBatis 会:

    1. 获取 MappedStatement 对象: 根据 Mapper 接口方法和方法名 (或 SQL 语句 ID) 找到对应的 MappedStatement 对象。
    2. 执行 SQL 查询: 使用 JDBC 执行 MappedStatement 中定义的 SQL 语句,获取 ResultSet (结果集)。
    3. 创建 ResultHandler 实例 (或使用默认的 DefaultResultHandler): ResultHandler 负责处理结果集。 MyBatis 通常使用默认的 DefaultResultHandler,也可以自定义 ResultHandler
    4. 获取 ResultMap 对象 (或 resultType):MappedStatement 对象中获取 ResultMap 对象 (如果配置了 resultMap) 或 resultType (如果配置了 resultType)。
    5. 逐行处理 ResultSet: ResultHandler 会逐行遍历 ResultSet,对于每一行数据:
      • 根据 ResultMap (或 resultType) 定义的映射规则,将 ResultSet 当前行的数据映射到 Java 对象。 这个映射过程会涉及到类型转换、属性赋值、关联对象/集合的创建和赋值等复杂操作。
      • 将映射后的 Java 对象添加到结果列表 (如果是 selectList) 或直接返回 (如果是 selectOne)。

    ResultHandlerResultMap 协同工作,完成了结果集到 Java 对象的映射过程。 ResultMap 定义映射规则,ResultHandler 负责执行映射操作。

总结:

  • XMLMapperBuilder 解析 SQL 语句时,会将 SQL 语句文本、参数映射、结果映射等信息封装成 MappedStatement 对象MappedStatement 是 MyBatis 运行时可执行的 SQL 对象。
  • XMLMapperBuilder 解析 <resultMap> 元素时,会构建 ResultMap 对象ResultMap 对象定义了结果集映射规则,但本身不执行映射操作。
  • ResultHandler 接口及其实现类 才是真正负责结果集映射的处理器。 MyBatis 运行时使用 ResultHandlerResultMap 协同工作,将查询结果集逐行映射到 Java 对象。
  • XMLMapperBuilder 的解析工作为 MyBatis 运行时执行 SQL 和进行结果集映射提供了必要的配置信息和对象模型。