设为首页 加入收藏

TOP

MyBatis 解析 XML 标签及占位符相关源码剖析(一)
2018-04-08 08:51:29 】 浏览:804
Tags:MyBatis 解析 XML 标签 占位 相关 源码 剖析

开端

今天小朋友X在开发过程中遇到了一个bug,并给mybatis提了一个ISSUE:throw ReflectionException when using #{array.length}

大致说明下该问题,在mapper.xml中,使用#{array.length}来获取数组的长度时,会报出ReflectionException。 代码:

public List<QuestionnaireSent> selectByIds(Integer[] ids) { 
    return commonSession.selectList("QuestionnaireSentMapper.selectByIds", ImmutableMap.of("ids", ids)); 
}

对应的xml:

<select id="selectByIds">
	SELECT * FROM t_questionnaire
	<if test="ids.length > 0">
		WHERE id in
		<foreach collection="ids" open="(" separator="," close=")" item="id">#{id}
		</foreach>
	</if>
	LIMIT #{ids.length}
</select>

下面结合源码对该问题进行分析

分析

xml中有两处使用了length,那么这个报错究竟是哪个引起的呢?

尝试把test条件去掉,limit保留后,依然报错。那么可定位出报错是#{ids.length}导致的。

由此引出了两个问题:

  1. XML标签中条件是如何解析的(扩展,foreach是如何解析的数组和集合)
  2. #{ids.length}是如何解析的

带着这两个问题,我们进入源码

第一部分 XML标签的解析

在类org.apache.ibatis.scripting.xmltags.XMLScriptBuilder中

private void initNodeHandlerMap() {
    nodeHandlerMap.put("trim", new TrimHandler());
    nodeHandlerMap.put("where", new WhereHandler());
    nodeHandlerMap.put("set", new SetHandler());
    nodeHandlerMap.put("foreach", new ForEachHandler());
    nodeHandlerMap.put("if", new IfHandler());
    nodeHandlerMap.put("choose", new ChooseHandler());
    nodeHandlerMap.put("when", new IfHandler());
    nodeHandlerMap.put("otherwise", new OtherwiseHandler());
    nodeHandlerMap.put("bind", new BindHandler());
}
protected MixedSqlNode 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 = nodeHandlerMap.get(nodeName);
      if (handler == null) {
        throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
      }
      handler.handleNode(child, contents);
      isDynamic = true;
    }
  }
  return new MixedSqlNode(contents);
}

在每个对应的Handler中,有相应的处理逻辑。

以IfHandler为例:

private class IfHandler implements NodeHandler {
  public IfHandler() {
    // Prevent Synthetic Access
  }

  @Override
  public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
    MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
    String test = nodeToHandle.getStringAttribute("test");
    IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
    targetContents.add(ifSqlNode);
  }
}

在这里主要生成了IfSqlNode,解析在相应的类中

public class IfSqlNode implements SqlNode {
  private final Expre
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java日志框架:logback详解 下一篇一次非典型性 Redis 阻塞总结

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目