XMLStatementBuilder.parseStatementNode 第四步
第四步就一句话,但是里面的内容很多,我们慢慢分析:
// Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver);
看英文注释,主要的作用就是解析 selectKey 并且在当前节点下移除掉。
selectKey 节点只可能在 insert 和 update 当中出现。
继续看详细代码:
private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) { List<XNode> selectKeyNodes = context.evalNodes("selectKey"); if (configuration.getDatabaseId() != null) { parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId()); } parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null); removeSelectKeyNodes(selectKeyNodes); }
先解析 selectKey,然后移除 selectKey。
private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) { for (XNode nodeToHandle : list) { String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX; String databaseId = nodeToHandle.getStringAttribute("databaseId"); if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) { parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId); } } }
循环全部的 selectKey 的节点,如果没有那么什么操作都不作。
private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) { String resultType = nodeToHandle.getStringAttribute("resultType"); // 当中省略了基本属性的解析 ResultSetType resultSetTypeEnum = null; SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass); SqlCommandType sqlCommandType = SqlCommandType.SELECT; builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null); id = builderAssistant.applyCurrentNamespace(id, false); MappedStatement keyStatement = configuration.getMappedStatement(id, false); configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore)); }
这个重载的方法里面主要做如下几个事情:
- 解析 selectKey 的基本属性
- 通过 langDriver 生成 SqlSource
- 创建 selectKey 查询语句的 KeyGenerator,添加到 configuration 当中
下面详细分析 langDriver 生成 SqlSource 的过程:
首先看 LanguageDriver 是一个接口:
public interface LanguageDriver { ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql); SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType); SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType); }
LanguageDriver 有三个方法,一个是创建参数 Handler,另外两个是创建 SqlSource。
LanguageDriver - XMLLanguageDriver - RawLanguageDriver
上面是 LanguageDriver 的继承结构。
当然在 Configuration 的构造方法当中,我们以及设置了默认的 Driver class : languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
在这个 parseSelectKeyNode 方法当中传递进来的也是默认的 XMLLanguageDriver 的实例。
下面就是 XMLLanguageDriver.createSqlSource 方法:
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); return builder. (); }
可以看到是通过创建 XMLScriptBuilder 实例来继而调用 parseScriptNode 方法获得到 SqlSource。
XMLScriptBuilder 同样继承了 BaseBuilder。
public class XMLScriptBuilder extends BaseBuilder { private XNode context; private boolean isDynamic; private Class<?> parameterType; }
下面是 parseScriptNode 方法:
public SqlSource parseScriptNode() { List<SqlNode> contents = parseDynamicTags(context); MixedSqlNode rootSqlNode = new MixedSqlNode(contents); SqlSource sqlSource = null; if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; } 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; }
首先判断当前的节点是不是有动态的 tag,动态的 tag 包括一些 where、choose 等。
看 parseDynamicTags 的代码的判断入下:
- 获取 SelectKey 的所有子节点
- 循环判断如果子节点的元素类型的 ELEMENT 那么这个一定是动态的,并且根据不同的元素生成不同的 NodeHandler
- 生成 SqlNode 的 list 并且返回
根据代码再来一遍流程:
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) { //如果是文本的子节点 String data = child.getStringBody("");//获取文本信息 TextSqlNode textSqlNode = new TextSqlNode(data);//创建TextSqlNode if (textSqlNode.isDynamic()) {//如果 sql 当中包含有未被属性替换的 ${} 字符串那么就是动态的 contents.add(textSqlNode);//添加到 list 当中 isDynamic = true; } else { contents.add(new StaticTextSqlNode(data)); } }
下面在看下 SqlNode 接口:
public interface SqlNode { boolean apply(DynamicContext context); }
TextSqlNode 和 StaticTextSqlNode 有时间,单独分析,他的体系,他们都实现了 SqlNode 接口。
继续看其他的 if 分支:
else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628 String nodeName = child.getNode().getNodeName();//获取子节点的name NodeHandler handler = nodeHandlers(nodeName);//根据name获取到NodeHandler if (handler == null) { throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); } handler.handleNode(child, contents); isDynamic = true; } 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); }
解析动态的 tag 并且获得了 SqlNode 的 list 之后,需要根据是否是动态的创建响应的 SqlSource:
public SqlSource parseScriptNode() { List<SqlNode> contents = parseDynamicTags(context); MixedSqlNode rootSqlNode = new MixedSqlNode(contents); SqlSource sqlSource = null; if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; }
SqlSource 以后仔细分析。
至此 XMLStatementBuilder.parseSelectKeyNode 的创建 SqlSource 已经完成。
方法下面就是通过 builderAssistant 创建 MappedStatement:
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null);
builderAssistant.addMappedStatement 里面代码就不贴了,基本上套路相同先创建 MappedStatement.Builder 实例,然后设置各种传递进来的参数,最后添加到 configuration 当中。
在 configuration 当中添加完 MappedStatement 后,继续在 configuration 当中添加 SelectKeyGenerator。
这样 XMLStatementBuilder.parseStatementNode 第四步完成。
XMLStatementBuilder.parseStatementNode 第五步
第五步比较简单就是创建当前 insert 或者 update 的 MappedStatement 对象,并且添加到 configuration 当中。
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
上面代码省略了一部分。
以后继续分析没有完善的内容
—EOF—