MyBatis 动态 SQL 的生成原理主要基于其灵活的 XML 配置解析机制和动态标签的递归处理逻辑,通过以下核心步骤实现 SQL 的动态拼接:
1. XML 配置解析
当 MyBatis 初始化时,会通过 XPath 解析器加载 Mapper XML 文件,提取 <select>、<insert> 等标签中的 SQL 语句。对于动态 SQL 标签(如 <if>、<choose>、<foreach>),MyBatis 会将其转换为特定的 SqlNode 对象,构建一个动态 SQL 节点树。
2. 动态标签的 SqlNode 实现
每个动态标签对应一个 SqlNode 接口的实现类,例如:
<if>→IfSqlNode<choose>→ChooseSqlNode<foreach>→ForEachSqlNode<where>→WhereSqlNode
这些 SqlNode 对象在解析时会被组织成树形结构,用于后续的动态拼接。
3. OGNL(Object-Graph Navigation Language)表达式求值
动态标签中的 test 属性(如 <if test="name != null">)使用 OGNL(对象图导航语言) 表达式进行条件判断。MyBatis 在运行时:
- 从参数对象中提取属性值。
- 通过
OgnlCache缓存解析表达式,提高性能。 - 根据表达式结果(
true/false)决定是否包含该 SQL 片段。
4. SQL 动态拼接过程
当执行 Mapper 方法时,MyBatis 会:
- 生成
SqlSource对象:根据 XML 配置构建DynamicSqlSource,封装动态 SQL 节点树。 - 获取
BoundSql对象:- 遍历
SqlNode树,递归处理每个节点。 - 对
<if>节点:判断条件,决定是否拼接子节点 SQL。 - 对
<foreach>节点:遍历集合,展开成IN子句或批量插入语句。 - 对
<where>节点:自动处理WHERE关键字和条件前的AND/OR。
- 遍历
- 参数替换:将
#{}占位符替换为?,并生成ParameterMapping对象,记录参数位置和类型。
5. 缓存机制
MyBatis 对解析后的 SqlSource 和 BoundSql 进行缓存,避免重复解析 XML。首次执行时生成并缓存,后续直接复用,提升性能。
示例:动态 SQL 生成流程
假设有以下 XML 配置:
<select id="findUser" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">AND name = #{name}</if>
<if test="age != null">AND age = #{age}</if>
</where>
</select>
生成过程:
- 解析
<where>标签为WhereSqlNode,内部包含两个IfSqlNode。 - 执行时,若
name和age均非空:WhereSqlNode自动添加WHERE关键字。- 两个
<if>条件成立,拼接AND name = ? AND age = ?。 - 最终 SQL:
SELECT * FROM user WHERE name = ? AND age = ?
- 若
age为空,则拼接为:SELECT * FROM user WHERE name = ?
关键设计思想
- 组合模式:通过
SqlNode树形结构实现动态 SQL 的灵活组合。 - 责任链模式:每个
SqlNode依次处理 SQL 片段,决定是否包含或跳过。 - 表达式语言:利用 OGNL 实现简洁的条件判断。
性能优化
- 预编译:动态 SQL 最终生成带占位符的 SQL,由 JDBC 预编译执行,防止 SQL 注入。
- 缓存:减少 XML 解析和表达式求值次数。
通过以上机制,MyBatis 实现了高效、灵活的动态 SQL 生成,适应复杂多变的查询场景。

被折叠的 条评论
为什么被折叠?



