上面的解析方法没有什么实质性的解析逻辑,我们继续往下分析。
public void parseStatementNode() { // 获取 id 和 databaseId 属性 String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); // 根据 databaseId 进行检测,检测逻辑和上一节基本一致,这里不再赘述 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } // 获取各种属性 Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute("resultMap"); String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); // 通过别名解析 resultType 对应的类型 Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); // 解析 Statement 类型,默认为 PREPARED StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); // 解析 ResultSetType ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); // 获取节点的名称,比如 <select> 节点名称为 select String nodeName = context.getNode().getNodeName(); // 根据节点名称解析 SqlCommandType SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // 解析 <include> 节点 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // 解析 <selectKey> 节点 processSelectKeyNodes(id, parameterTypeClass, langDriver); // 解析 SQL 语句 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 实例 keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { // 创建 KeyGenerator 实例 keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } /* * 构建 MappedStatement 对象,并将该对象存储到 * Configuration 的 mappedStatements 集合中 */ builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }上面的代码比较长,看起来有点复杂。不过如果大家耐心看一下源码,会发现,上面的代码中起码有一般的代码都是用来获取节点属性,以及解析部分属性等。抛去这部分代码,以上代码做的事情如下。
解析 <include> 节点
解析 <selectKey> 节点
解析 SQL,获取 SqlSource
构建 MappedStatement 实例
以上流程对应的代码比较复杂,每个步骤都能分析出一些东西来。下面我会每个步骤都进行分析,首先来分析 <include> 节点的解析过程。
2.1.5.1 解析 <include> 节点<include> 节点的解析逻辑封装在 applyIncludes 中,该方法的代码如下:
public void applyIncludes(Node source) { Properties variablesContext = new Properties(); Properties configurationVariables = configuration.getVariables(); if (configurationVariables != null) { // 将 configurationVariables 中的数据添加到 variablesContext 中 variablesContext.putAll(configurationVariables); } // 调用重载方法处理 <include> 节点 applyIncludes(source, variablesContext, false); }