SqlNode
每个 XML Node 会解析成对应的 SQL Node 对象。
public interface SqlNode {
//将各Sql片段合并到DynamicContext中,拼接称为完整的SQL
boolean apply(DynamicContext context);
}
apply
方法会根据传入的参数context,参数解析该SqlNode所记录的SQL片段,并调用DynamicContext.appendSql()
方法将解析后的SQL片段追加到DynamicContext.的sqlBuilder
中保存。- 当SQL节点下的所有SqlNode完成解析后,可以通过
DynamicContext.getSql()
获取一条完成的SQL语句。
0、创建SqlSource过程中获取SQL
//☆☆--RawSqlSource
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
/**
* 通过遍历所有的SqlNode,获取sql
*/
private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
DynamicContext context = new DynamicContext(configuration, null);
//rootSqlNode为MixedSqlNode
rootSqlNode.apply(context);
return context.getSql();
}
rootSqlNode.apply(context)
开始解析MixedSqlNode中记录的SQL片段。
//☆☆--MixedSqlNode
public class MixedSqlNode implements SqlNode {
//记录sql节点中的所有SQL片段
private final List<SqlNode> contents;
public MixedSqlNode(List<SqlNode> contents) {
this.contents = contents;
}
@Override
public boolean apply(DynamicContext context) {
//NOTE: 循环调用contents集合中的所有SqlNode对象的apply方法
contents.forEach(node -> node.apply(context));
return true;
}
}
- 通过解析sql节点得到的MixedSqlNode结构如下:
- 如下的sql配置构建的MixedSqlNode:
<select id="testSelect" resultMap="selectAuthor">
SELECT
id, username, password, email, bio, favourite_section
FROM author
id = 101
<where>
<if test="username != null">
AND username = #{username}
</if>
</where>
</select>
1、StaticTextSqlNode
最简单的SqlNode,功能仅仅就是将自身记录的text拼接到context上下文中。
//StaticTextSqlNode
public boolean apply(DynamicContext context) {
context.appendSql(text);
return true;
}
2、TextSqlNode
- 表示包含 “${}” 占位符的动态SQL节点。
TextSqlNode.apply()
方法会使用GenericTokenParser解析“${}”占位符,并直接替换成传入的实际参数值。- 实际参数值获取逻辑在内部
BindingTokenParser.handleToken
中。
3、IfSqlNode
if标签平常用的最多,在处理对应节点逻辑时,其主要工作就是通过Ognl表达式和传入的参数进行判断,看传入的参数值是否有满足if test里的表达式,满足将SQL片段合并到context上下文中。若不满足test则过滤掉这一部分SQL片段,不添加到context中。
4、WhereSqlNode、SetSqlNode
<where /> 标签的 SqlNode 实现类,继承至WhereSqlNode。
<set /> 标签的 SqlNode 实现类,继承至SetSqlNode。
- WhereSqlNode会传入
prefix=WHERE
和prefixesToOverride=["AND ","OR ","AND\n", "OR\n", "AND\r", "OR\r", "AND\t", "OR\t"]
- SetSqlNode会传入
prefix=WHERE
和prefixesToOverride=[“,”]
- 所以说trim和where、set是同一个套路
5、TrimSqlNode
<trim /> 标签的 SqlNode 实现类
private final String prefix;
private final String suffix;
/**
* 需要被删除的前缀
*/
private final List<String> prefixesToOverride;
/**
* 需要删除的后缀
*/
private final List<String> suffixesToOverride;
6、ForEachSqlNode
<foreach /> 标签的 SqlNode 实现类
7、VarDeclSqlNode
<bind /> 标签的 SqlNode 实现类,只要通过
public boolean apply(DynamicContext context) {
//NOTE: OGNL表达式
final Object value = OgnlCache.getValue(expression, context.getBindings());
//NOTE: 绑定到上下文(记录到ContextMap中)
context.bind(name, value);
return true;
}