dynamicsqlsource是动态sql执行入口,通过sqlnode树递归apply生成sql,再经sqlsourcebuilder解析#{}占位符,最终封装为boundsql容器;boundsql仅承载运行时生成的sql字符串、参数映射等结果,不参与解析。

BoundSql 本身不解析动态 SQL 标签,它只是最终 SQL 的“容器”。真正完成动态标签解析和 SQL 拼装的是 DynamicSqlSource 配合 SqlNode 和 DynamicContext 在运行时协同完成的。
DynamicSqlSource 是动态 SQL 的执行入口
当 MyBatis 执行 SQL(如调用 MappedStatement.getBoundSql(param))时,若该语句对应的是动态 SQL(含 <if></if>、<where></where>、<foreach></foreach> 等),其 SqlSource 实例就是 DynamicSqlSource。它的 getBoundSql() 方法会:
- 新建一个
DynamicContext,传入参数对象并初始化上下文(含_parameter绑定) - 调用根
SqlNode.apply(context)—— 这是递归遍历整个 XML 解析树的核心步骤 - 此时所有
SqlNode(如IfSqlNode、WhereSqlNode、ForEachSqlNode)根据 OGNL 表达式判断是否生效,并将有效 SQL 片段追加到context.getSql()的StringBuilder中 - 拼接完成后,用
SqlSourceBuilder对已生成的字符串再次解析(处理#{}占位符、提取ParameterMapping),最终封装为BoundSql
SqlNode 是动态标签的抽象单元
每个 XML 动态标签在启动阶段就被解析为对应的 SqlNode 实现类:
-
IfSqlNode:执行test表达式,为 true 则调用子节点apply -
WhereSqlNode:先 apply 子节点,再对结果做前导/尾随空格与 AND/OR 清理 -
ForEachSqlNode:遍历集合,为每个元素生成一次子节点内容,用分隔符连接 -
MixedSqlNode:作为容器,按顺序执行多个子SqlNode
这些节点构成一棵树,DynamicSqlSource 在每次执行时才触发整棵树的 apply,因此 SQL 是**运行时动态生成**的,不是配置时就确定的。
BoundSql 只负责承载最终结果
生成后的 BoundSql 对象包含以下关键字段:
-
sql:已完全拼好的、不含任何标签的原始 SQL 字符串(例如"SELECT * FROM user WHERE name = ? AND age > ?") -
parameterMappings:从#{}中提取的参数映射列表,描述每个 ? 对应的属性路径、JDBC 类型等 -
parameterObject:原始传入的参数(Map / POJO / 单值) -
additionalParameters:动态过程中额外注入的上下文变量(如_databaseId)
它不保存任何解析逻辑,也不参与条件判断——只记录“这一轮执行到底用了哪条 SQL、有哪些参数”。
静态 SQL 和动态 SQL 的 BoundSql 来源不同
注意区分两种情况:
- 纯静态 SQL(无任何动态标签)→ 使用
RawSqlSource→ 启动时就解析好StaticSqlSource→getBoundSql()直接返回预编译结果 - 含动态标签 → 使用
DynamicSqlSource→ 每次调用getBoundSql()都重新走一遍SqlNode.apply + SqlSourceBuilder.parse流程 → 每次可能生成不同 SQL
这也是为什么日志中看到的 SQL 可能随参数变化而不同——BoundSql 的内容取决于当前参数下 DynamicSqlSource 的实时计算结果。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











