自学内容网 自学内容网

Mybatis动态标签解析

# 表达式解析

mybatis框架中解析动态表达式采用了ognl框架实现,但其封装了两个类:

OgnlCache:用于缓存表达式解析结果

ExpressionEvaluator:实际解析表达式结果

我们可以通过以下代码来测试表达式的解析结果:  

  public static void testOgnl() {

        User user = new User();

        user.setId("wyt");

        user.setName("wyt");

        String expression = "user.id != null and user.id != ''";

        Map<String, Object> context = new ConcurrentHashMap<>(10);

        context.put("user", user);

        ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator();

        boolean ret = expressionEvaluator.evaluateBoolean(expression, context);

        System.out.println("ret:" + ret);

}

输出结果为:

ret:true

# 动态标签解析与执行

XMLScriptBuilder.java中将各类标签封装成类(如:IfHandler类):

NodeHandler nodeHandlers(String nodeName) {

    Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();

    map.put("trim", new TrimHandler());

    map.put("where", new WhereHandler());

    map.put("set", new SetHandler());

    map.put("foreach", new ForEachHandler());

    map.put("if", new IfHandler());

    map.put("choose", new ChooseHandler());

    map.put("when", new IfHandler());

    map.put("otherwise", new OtherwiseHandler());

    map.put("bind", new BindHandler());

    return map.get(nodeName);

  }

如果获取到子节点中包含节点,则说明为动态节点:

List<SqlNode> parseDynamicTags(XNode node) {

    List<SqlNode> contents = new ArrayList<SqlNode>();

    NodeList children = node.getNode().getChildNodes();

    for (int i = 0; i < children.getLength(); i++) {

      XNode child = node.newXNode(children.item(i));

      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {

        String data = child.getStringBody("");

        TextSqlNode textSqlNode = new TextSqlNode(data);

        if (textSqlNode.isDynamic()) {

          contents.add(textSqlNode);

          isDynamic = true;

        } else {

          contents.add(new StaticTextSqlNode(data));

        }

      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628   这里是动态节点解析

        String nodeName = child.getNode().getNodeName();

        NodeHandler handler = nodeHandlers(nodeName);

        if (handler == null) {

          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");

        }

        handler.handleNode(child, contents);

        isDynamic = true;

      }

    }

    return contents;

  }

IfNode解析后生成IfSqlNode对象: 

private class IfHandler implements NodeHandler {

    public IfHandler() {

      // Prevent Synthetic Access

    }



    @Override

public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {

           //动态标签则为包含预填充参数的标签

      List<SqlNode> contents = parseDynamicTags(nodeToHandle);

      MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);

      String test = nodeToHandle.getStringAttribute("test");

      IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);

      targetContents.add(ifSqlNode);

    }

  }

解析完成就是执行标签了,执行标签的时候尤其是表达式则通过ognl表达式解析: 

public IfSqlNode(SqlNode contents, String test) {

    this.test = test;

    this.contents = contents;

    this.evaluator = new ExpressionEvaluator();

  }



  @Override

  public boolean apply(DynamicContext context) {

    if (evaluator.evaluateBoolean(test, context.getBindings())) {

      contents.apply(context);

      return true;

    }

    return false;

  }

apply方法实现如下:

@Override

  public boolean apply(DynamicContext context) {

    GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));

    context.appendSql(parser.parse(text));

    return true;

  }

当执行结果完成后如何影响生成的SQL语句呢?如果是一个动态的语句,解析完成后追加SQL

@Override

  public boolean apply(DynamicContext context) {

    GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));

    context.appendSql(parser.parse(text));

    return true;

  }

SQL语句是追加到最终执行的语句了,但还有预填充的值,这个该如何处理呢?BindingTokenParser解析就是将预填充参数写入到最终参数中。

@Override

    public String handleToken(String content) {

      Object parameter = context.getBindings().get("_parameter");

      if (parameter == null) {

        context.getBindings().put("value", null);

      } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {

        context.getBindings().put("value", parameter);

      }

      Object value = OgnlCache.getValue(content, context.getBindings());

      String srtValue = (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"

      checkInjection(srtValue);

      return srtValue;

}

如果是foreachNode则会预填充很多的参数及SQL语句以及展开的参数信息。


原文地址:https://blog.csdn.net/u014291956/article/details/145089349

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!