MyBatis 动态 SQL 底层实现分析
1. XML 解析流程
MyBatis 通过 XMLScriptBuilder 类解析动态 SQL 标签,核心步骤:
public class XMLScriptBuilder {
private final XNode context; // XML节点
private final Map<String, NodeHandler> nodeHandlers = new HashMap<>();
public XMLScriptBuilder() {
// 注册标签处理器
nodeHandlers.put("if", new IfHandler());
nodeHandlers.put("foreach", new ForEachHandler());
// ...其他标签
}
public SqlSource parseScriptNode() {
// 递归解析节点树
MixedSqlNode rootSqlNode = parseDynamicTags(context);
return new DynamicSqlSource(configuration, rootSqlNode);
}
}
2. 标签解析原理
每种标签对应特定处理器:
<if>标签:生成IfSqlNodeprivate class IfHandler implements NodeHandler { public void handleNode(XNode node, List<SqlNode> targetContents) { // 解析test表达式 String test = node.getStringAttribute("test"); SqlNode contents = parseDynamicTags(node); targetContents.add(new IfSqlNode(contents, test)); } }<foreach>标签:生成ForEachSqlNode实现集合迭代:$S = {s_1, s_2, \dots, s_n}$ 时生成 $n$ 个占位符
3. SQL 拼接机制
运行时通过 DynamicSqlSource 构建最终 SQL:
public class DynamicSqlSource implements SqlSource {
private final SqlNode rootSqlNode;
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(parameterObject);
rootSqlNode.apply(context); // 递归应用所有SqlNode
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder();
return sqlSourceParser.parse(context.getSql());
}
}
关键类说明:
| 类名 | 职责 |
|---|---|
SqlNode | 动态节点抽象接口 |
StaticTextSqlNode | 处理静态文本片段 |
MixedSqlNode | 组合多个SqlNode的容器 |
DynamicContext | 存储拼接过程中的SQL缓冲区 |
4. 表达式引擎
使用 OGNL 表达式处理条件判断,例如:
<if test="name != null">转换为布尔表达式:$\text{name} \neq \text{null}$- 运行时计算表达式值:$E(\text{test}) \in {0,1}$
5. SQL 生成示例
对于以下动态 SQL:
<select id="findUser">
SELECT * FROM users
<where>
<if test="name != null"> name = #{name} </if>
<if test="age > 18"> AND age > #{age} </if>
</where>
</select>
运行时拼接过程:
- 解析为
WhereSqlNode(MixedSqlNode(IfSqlNode(...)))结构 - 当参数满足 $\text{name} \neq \emptyset \land \text{age} > 18$ 时:
SELECT * FROM users WHERE name = ? AND age > ? - 自动移除多余的
AND关键字(WhereSqlNode特性)
6. 性能优化策略
- 预处理机制:首次解析后缓存
SqlSource对象 - 参数绑定:使用
ParameterMappingTokenHandler处理 $#{}$ 占位符 - 批量处理:
<foreach>标签预计算迭代次数 $n$,避免运行时反射开销
源码路径:
org.apache.ibatis.scripting.xmltags包实现动态 SQL 核心逻辑,通过组合设计模式实现标签的无限嵌套能力。
1164

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



