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
对象,并将其关联到MappedStatement
。parameterMap
方式已经过时,不推荐使用。- 内联参数映射 (
#{}
和${}
): 在 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
对象,并将其关联到MappedStatement
。resultMap
方式适用于复杂的结果集映射场景 (例如关联查询、集合属性、多态映射等)。resultType
属性: 通过<select>
元素的resultType
属性直接指定结果类型。resultType
方式适用于简单的结果集映射场景 (例如单表查询,结果类型是基本类型或 POJO)。 MyBatis 会自动进行简单的属性映射。
XMLMapperBuilder
会根据<select>
元素配置的resultMap
或resultType
属性, 创建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. 注册
MappedStatement
到Configuration
:XMLMapperBuilder
会将构建好的MappedStatement
对象 注册到Configuration
对象的mappedStatements
属性 (一个StrictMap<MappedStatement>
) 中。 注册时,会使用 Mapper 接口的全限定名 + SQL 语句的id
属性作为MappedStatement
的唯一 ID (mappedStatementId)。
2. XMLMapperBuilder
如何将结果映射规则解析成对应的处理器 (ResultMap 和 ResultHandler):
XMLMapperBuilder
在解析 <resultMap>
元素时,会构建 ResultMap
对象。 ResultMap
对象本身 不是处理器 (Handler),而是 结果映射规则的定义。 它描述了如何将查询结果集中的列映射到 Java 对象的属性。
真正的结果集映射处理器是 ResultHandler
接口的实现类。 ResultHandler
接口负责 逐行处理查询结果集,并将每一行数据按照 ResultMap
或 resultType
定义的映射规则,映射到 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
对象) 中,使用resultMap
的id
作为 key。 -
2.3. 运行时,MyBatis 使用
ResultHandler
和ResultMap
进行结果集映射:在 MyBatis 运行时执行
SqlSession.selectList()
,SqlSession.selectOne()
等查询方法时,MyBatis 会:- 获取
MappedStatement
对象: 根据 Mapper 接口方法和方法名 (或 SQL 语句 ID) 找到对应的MappedStatement
对象。 - 执行 SQL 查询: 使用 JDBC 执行
MappedStatement
中定义的 SQL 语句,获取ResultSet
(结果集)。 - 创建
ResultHandler
实例 (或使用默认的DefaultResultHandler
):ResultHandler
负责处理结果集。 MyBatis 通常使用默认的DefaultResultHandler
,也可以自定义ResultHandler
。 - 获取
ResultMap
对象 (或resultType
): 从MappedStatement
对象中获取ResultMap
对象 (如果配置了resultMap
) 或resultType
(如果配置了resultType
)。 - 逐行处理
ResultSet
:ResultHandler
会逐行遍历ResultSet
,对于每一行数据:- 根据
ResultMap
(或resultType
) 定义的映射规则,将ResultSet
当前行的数据映射到 Java 对象。 这个映射过程会涉及到类型转换、属性赋值、关联对象/集合的创建和赋值等复杂操作。 - 将映射后的 Java 对象添加到结果列表 (如果是
selectList
) 或直接返回 (如果是selectOne
)。
- 根据
ResultHandler
和ResultMap
协同工作,完成了结果集到 Java 对象的映射过程。ResultMap
定义映射规则,ResultHandler
负责执行映射操作。 - 获取
总结:
XMLMapperBuilder
解析 SQL 语句时,会将 SQL 语句文本、参数映射、结果映射等信息封装成MappedStatement
对象,MappedStatement
是 MyBatis 运行时可执行的 SQL 对象。XMLMapperBuilder
解析<resultMap>
元素时,会构建ResultMap
对象,ResultMap
对象定义了结果集映射规则,但本身不执行映射操作。ResultHandler
接口及其实现类 才是真正负责结果集映射的处理器。 MyBatis 运行时使用ResultHandler
和ResultMap
协同工作,将查询结果集逐行映射到 Java 对象。XMLMapperBuilder
的解析工作为 MyBatis 运行时执行 SQL 和进行结果集映射提供了必要的配置信息和对象模型。