在这三步中,第1步和第2步调用的是公共逻辑,其他地方也会调用,这两步对应的源码后续会分两节进行讲解。第3步则是创建一个 SelectKeyGenerator 实例,SelectKeyGenerator 创建的过程本身没什么好说的,所以就不多说了。下面分析一下 SqlSource 和 MappedStatement 实例的创建过程。
2.1.5.3 解析 SQL 语句前面分析了 <include> 和 <selectKey> 节点的解析过程,这两个节点解析完成后,都会以不同的方式从 dom 树中消失。所以目前的 SQL 语句节点由一些文本节点和普通节点组成,比如 <if>、<where> 等。那下面我们来看一下移除掉 <include> 和 <selectKey> 节点后的 SQL 语句节点是如何解析的。
// -☆- XMLLanguageDriver public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); return builder.parseScriptNode(); } // -☆- XMLScriptBuilder public SqlSource parseScriptNode() { // 解析 SQL 语句节点 MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource = null; // 根据 isDynamic 状态创建不同的 SqlSource if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; }如上,SQL 语句的解析逻辑被封装在了 XMLScriptBuilder 类的 parseScriptNode 方法中。该方法首先会调用 parseDynamicTags 解析 SQL 语句节点,在解析过程中,会判断节点是是否包含一些动态标记,比如 ${} 占位符以及动态 SQL 节点等。若包含动态标记,则会将 isDynamic 设为 true。后续可根据 isDynamic 创建不同的 SqlSource。下面,我们来看一下 parseDynamicTags 方法的逻辑。
/** 该方法用于初始化 nodeHandlerMap 集合,该集合后面会用到 */ 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 isDynamic = true; } else { // 创建 StaticTextSqlNode contents.add(new StaticTextSqlNode(data)); } // child 节点是 ELEMENT_NODE 类型,比如 <if>、<where> 等 } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // 获取节点名称,比如 if、where、trim 等 String nodeName = child.getNode().getNodeName(); // 根据节点名称获取 NodeHandler NodeHandler handler = nodeHandlerMap.get(nodeName); /* * 如果 handler 为空,表明当前节点对与 MyBatis 来说,是未知节点。 * MyBatis 无法处理这种节点,故抛出异常 */ if (handler == null) { throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); } // 处理 child 节点,生成相应的 SqlNode handler.handleNode(child, contents); // 设置 isDynamic 为 true isDynamic = true; } } return new MixedSqlNode(contents); }