手写MyBatis ORM框架(八)

手写MyBatis ORM框架(八)

引言

在我们上一篇文章,完成了反射工具类,在这篇文章,我们要实现对xml文件中SQL的解析。

实现目标

在XMLConfigBuilder中完成对SQL的解析。

在我们之间,已经把环境解析完成了,我们这回就在这里完成SQL解析。

// 环境

environmentsElement(root.element("environments"));

流程图

解析标签

在XMLConfigBuilder中创建mapperElement方法,我们在这里解析所有的mapper标签

<mappers>

  • <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>

  • <mapper resource="org/mybatis/builder/BlogMapper.xml"/>

  • <mapper resource="org/mybatis/builder/PostMapper.xml"/>

<mappers>

每一个mapper标签都会被XMLMapperBuilder.parse方法解析。

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 字段映射配置(当数据库字段与Java属性不一致时使用) -->
    <resultMap id="userResultMap" type="com.example.entity.User">
        <id     property="id"        column="user_id" />       <!-- 主键映射 -->
        <result property="username"  column="user_name" />     <!-- 普通字段映射 -->
        <result property="email"     column="user_email" />
        <result property="createTime" column="create_time" /> <!-- 驼峰命名自动映射可不配置 -->
    </resultMap>
    <!-- 示例1:基础查询(带条件) -->
    <select id="selectUserById" resultMap="userResultMap">
        SELECT user_id, user_name, user_email, create_time
        FROM users
        WHERE user_id = #{userId}
    </select>
</mapper>

正常我们会先解析 cacheElement, resultMapElements,buildStatementFromContext

我们这里就先实现buildStatementFromContext。

XMLStatementBuilder

在这里我们就要使用上面这个构建了。

在这里我们就会开始解析出一个SQL。

解析SQL标签

问题来了,我们在这里都会解析些什么呢?


1. 解析 SQL 标签的基本属性

​属性名

​作用

​示例

id

SQL 操作的唯一标识符,对应 Mapper 接口的方法名。

id="selectUserById"

parameterType

参数类型的全限定名或别名(可选),MyBatis 会自动推断。

parameterType="java.lang.Long"

resultType

返回结果类型的全限定名或别名(与 resultMap 二选一)。

resultType="com.example.User"

resultMap

引用已定义的 <resultMap> 的 ID(与 resultType 二选一)。

resultMap="userResultMap"

databaseId

指定该 SQL 对应的数据库厂商 ID(用于多数据库支持)。

databaseId="mysql"

lang

指定自定义语言驱动(如支持其他脚本语言)。

lang="velocity"

timeout

设置数据库操作超时时间(单位:秒)。

timeout="30"

fetchSize

设置 JDBC 的 fetchSize 参数,控制批量获取的行数。

fetchSize="100"

statementType

指定 Statement 类型(STATEMENT, PREPARED, CALLABLE)。

statementType="PREPARED"

resultSetType

设置结果集类型(FORWARD_ONLY, SCROLL_INSENSITIVE, SCROLL_SENSITIVE)。

resultSetType="FORWARD_ONLY"

flushCache

是否清空本地缓存和二级缓存(默认:false,select 为 false,其他为 true)。

flushCache="true"

useCache

是否将结果存入二级缓存(仅对 select 有效,默认 true)。

useCache="false"

resultOrdered

是否强制按嵌套顺序处理结果集(用于复杂嵌套结果映射)。

resultOrdered="true"

resultSets

指定多结果集的名称(用于存储过程返回多个结果集的情况)。

resultSets="users,orders"


2. 解析 SQL 内容与动态标签

  • 动态 SQL 处理:解析 <if>, <where>, <foreach>, <trim> 等标签,生成 SqlSource 对象。

  • xml

<select id="findUser">
  SELECT * FROM user
  <where>
    <if test="name != null">AND name = #{name}</if>
    <if test="age != null">AND age = #{age}</if>
  </where>
</select>

3. 主键生成策略

​属性名

​作用

useGeneratedKeys

是否使用 JDBC 的 getGeneratedKeys 方法获取自增主键(默认 false)。

keyProperty

指定主键对应的 Java 对象属性名(如 id)。

keyColumn

指定数据库表中的主键列名(当列名与属性名不一致时使用)。

示例

xml

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id" keyColumn="user_id">
  INSERT INTO users (name) VALUES (#{name})
</insert>

4. 参数映射(ParameterMap)

  • 过时配置:MyBatis 3 已废弃 <parameterMap>,推荐内联参数映射。

  • 内联参数处理:通过 #{property} 自动映射参数,可指定 jdbcTypetypeHandler

  • xml

<select id="selectUser" resultType="User">
  SELECT * FROM user WHERE id = #{id, jdbcType=BIGINT, typeHandler=MyTypeHandler}
</select>

5. 结果映射(ResultMap)

  • 引用外部 <resultMap>:通过 resultMap 属性指定已定义的映射规则。

  • 自动映射:若未指定 resultMapresultType,MyBatis 尝试通过列名与属性名自动映射。


6. 缓存相关配置

​属性名

​作用

flushCache

执行后是否清空一级缓存(LocalCache)和二级缓存(true/false)。

useCache

是否将查询结果存入二级缓存(仅对 <select> 有效)。


7. 其他高级配置

​配置项

​作用

resultSets

存储过程返回多个结果集时,指定各结果集名称(逗号分隔)。

lang

指定自定义语言驱动(如集成 Velocity 模板)。

timeout

设置数据库驱动等待响应的超时时间(秒)。


8. 解析流程总结

  1. 读取基础属性:从 SQL 标签中提取 id, parameterType, resultType 等属性。

  2. 处理动态 SQL:通过 XMLScriptBuilder 解析动态标签,生成 SqlSource

  3. 主键策略配置:解析 useGeneratedKeys, keyProperty, keyColumn

  4. 缓存配置:设置 flushCacheuseCache

  5. 构建 MappedStatement:将解析结果封装为 MappedStatement 并注册到 Configuration

最后 这些内容都会组装成一个mapperStatement,存在configuration之中。

生成SqlSource

获取默认语言驱动器并解析SQL的操作。其实这部分就是 XML 脚步语言驱动器所实现的功能,在 XMLScriptBuilder 中处理静态SQL和动态SQL。

定义脚本语言驱动接口,提供创建 SQL 信息的方法,入参包括了配置、元素、参数。其实它的实现类一共有3个;XMLLanguageDriverRawLanguageDriverVelocityLanguageDriver,这里我们只是实现了默认的第一个即可。

大家可以参考这里的流程图,这个是总流程图的部分。

LICENSED UNDER CC BY-NC-SA 4.0