MyBatis源码通~SqlNode

    xiaoxiao2021-04-15  296

    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; }

    最新回复(0)