聊聊Mybatis的动态Sql之这三个SqlNode同样重要

举报
周杰伦本人 发表于 2022/08/30 12:37:30 2022/08/30
【摘要】 聊聊Mybatis的动态Sql之这三个SqlNode同样重要 ForEachSqlNode VarDeclSqlNode ChooseSqlNode 总结 聊聊Mybatis的动态Sql之这三个SqlNode同样重要SqlNode作为一个接口,是动态SQL的有力支撑,这篇文章我们介绍一下SqlNode的三个重要的实现类。 ForEachSqlNodeforeach标签在我们编写Mapper...

聊聊Mybatis的动态Sql之这三个SqlNode同样重要

SqlNode作为一个接口,是动态SQL的有力支撑,这篇文章我们介绍一下SqlNode的三个重要的实现类。

ForEachSqlNode

foreach标签在我们编写Mapper文件的时候经常用到,而ForEachSqlNode类就是用来解析出<foreach> 的对象的

ForEachSqlNode的apply()方法:

@Override
  public boolean apply(DynamicContext context) {
    Map<String, Object> bindings = context.getBindings();
    final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings,
      Optional.ofNullable(nullable).orElseGet(configuration::isNullableOnForEach));
    if (iterable == null || !iterable.iterator().hasNext()) {
      return true;
    }
    boolean first = true;
    applyOpen(context);
    int i = 0;
    for (Object o : iterable) {
      DynamicContext oldContext = context;
      if (first || separator == null) {
        context = new PrefixedContext(context, "");
      } else {
        context = new PrefixedContext(context, separator);
      }
      int uniqueNumber = context.getUniqueNumber();
      // Issue #709
      if (o instanceof Map.Entry) {
        @SuppressWarnings("unchecked")
        Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;
        applyIndex(context, mapEntry.getKey(), uniqueNumber);
        applyItem(context, mapEntry.getValue(), uniqueNumber);
      } else {
        applyIndex(context, i, uniqueNumber);
        applyItem(context, o, uniqueNumber);
      }
      contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));
      if (first) {
        first = !((PrefixedContext) context).isPrefixApplied();
      }
      context = oldContext;
      i++;
    }
    applyClose(context);
    context.getBindings().remove(item);
    context.getBindings().remove(index);
    return true;
  }
  1. 解析<foreach> 的collection属性的表达式,得到集合,添加open属性的字符串
  2. 遍历集合,构建PrefixedContext对象,如果是Map类型,将key value保存到PrefixedContext对象中,不是的话就将集合元素下标志和集合元素保存到PrefixedContext对象中,调用SqlNode的apply()方法,处理#{}占位符
  3. 调用applyClose()方法添加close属性后缀
  4. 删除DynamicContext对象中的index和item

VarDeclSqlNode

有时候在编写Mapper文件的时候,bind绑定的标签也会用到,而
VarDeclSqlNode类是用来解析<bind>标签的。下面我们就看一下这个类是怎么定义的
VarDeclSqlNode类

public class VarDeclSqlNode implements SqlNode {

  private final String name;
  private final String expression;

  public VarDeclSqlNode(String var, String exp) {
    name = var;
    expression = exp;
  }

  @Override
  public boolean apply(DynamicContext context) {
    final Object value = OgnlCache.getValue(expression, context.getBindings());
    context.bind(name, value);
    return true;
  }

}

name保存了bind标签的name属性值,value保存bind标签的value属性值,apply()方法中利用OGNL解析表达式的值,然后把name和value绑定到DynamicContext中,这样就能根据name获取到value值了。

ChooseSqlNode

Mapper文件可以设置动态的sql,根据条件选择不同的sql片段,而choose标签就是实现这个功能的,
ChooseSqlNode是解析出choose标签的类
ChooseSqlNode的类的定义如下:

public class ChooseSqlNode implements SqlNode {
  private final SqlNode defaultSqlNode;
  private final List<SqlNode> ifSqlNodes;

  public ChooseSqlNode(List<SqlNode> ifSqlNodes, SqlNode defaultSqlNode) {
    this.ifSqlNodes = ifSqlNodes;
    this.defaultSqlNode = defaultSqlNode;
  }

  @Override
  public boolean apply(DynamicContext context) {
    for (SqlNode sqlNode : ifSqlNodes) {
      if (sqlNode.apply(context)) {
        return true;
      }
    }
    if (defaultSqlNode != null) {
      defaultSqlNode.apply(context);
      return true;
    }
    return false;
  }
}

它的apply()方法也比较简单:遍历所有ifSqlNodes,也是when标签的对象信息,调用各自的apply()方法,返回成功表示when条件成立,就返回true,defaultSqlNode不为空也是true,其他为false

总结

这篇文章主要讲了解析foreach标签的类ForEachSqlNode、解析<bind>标签的VarDeclSqlNode类和解析choose标签的ChooseSqlNode类,同时介绍这三个类的apply()方法的实现,这三个类都是SqlNode的实现类,都是对xml文件中的标签解析成的对象,这些SqlNode对象会被解析处理成SqlSource接口和它的实现类,供数据库执行,下篇文章我们将介绍SqlSource接口和它的实现类

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。