MyBatis 动态 SQL 的底层实现:XML 标签解析与 SQL 拼接的源码分析

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> 标签:生成 IfSqlNode
    private 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>

运行时拼接过程:

  1. 解析为 WhereSqlNode(MixedSqlNode(IfSqlNode(...))) 结构
  2. 当参数满足 $\text{name} \neq \emptyset \land \text{age} > 18$ 时:
    SELECT * FROM users WHERE name = ? AND age > ?
    

  3. 自动移除多余的 AND 关键字(WhereSqlNode 特性)
6. 性能优化策略
  • 预处理机制:首次解析后缓存 SqlSource 对象
  • 参数绑定:使用 ParameterMappingTokenHandler 处理 $#{}$ 占位符
  • 批量处理<foreach> 标签预计算迭代次数 $n$,避免运行时反射开销

源码路径:org.apache.ibatis.scripting.xmltags 包实现动态 SQL 核心逻辑,通过组合设计模式实现标签的无限嵌套能力。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值