在 mybatis 中, 对应 CRUD 的是四种节点: <select>, <insert>, <delete>, <update>。
在解析 Mapper.xml 文件中, 会调用 XMLStatementBuilder 来 进行这几个节点的解析。 解析完成后使用 MappedStatement 来表示一条条 SQL 语句。 完成的是这样这个过程
0在此之前, 需要先了解一下 <sql>。
<sql> 节点不仅仅是代码生成器生成时, 代表一些字段而已, 其定义可重用的 SQL 语句的片段。 类似于我们在写代码时, 抽象出一个方法。
/** * 解析 <sql> 节点 * * @param list * @param requiredDatabaseId * @throws Exception */ private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception { // 遍历 <sql> 节点 for (XNode context : list) { // 获取 databaseId 属性 String databaseId = context.getStringAttribute("databaseId"); // 获取 id 属性 String id = context.getStringAttribute("id"); // 为 id 添加命名空间 id = builderAssistant.applyCurrentNamespace(id, false); // 检查 sql 节点的 databaseId 与当前 Configuration 中的是否一致 if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) { // 记录到 XMLMapperBuider.sqlFragments(Map<String, XNode>)中保存 // 其最终是指向了 Configuration.sqlFragments(configuration.getSqlFragments) 集合 sqlFragments.put(id, context); } } }整体的过程就是获取所有节点, 然后逐个解析。 然后以 id-> context 键值对的方式存放在 XMLMapperBuilder.sqlFragments 对象中, 后续会用到。
注意, 此时的 context 还是 XNode 对象, 其最终的解析还是在解析 include 时进行解析。
注意, id 使用了 MapperBuilderAssistant.applyCurrentNamespace 进行了处理。 其是按照一定的规则在前面添加 namespace, 以便 id 在全局具有唯一性。
1 解析流程其整体的代码是这样子的
public void parseStatementNode() { // 获取 id 属性 String id = context.getStringAttribute("id"); // 获取 databaseid 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); Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); // 获取节点的类型 String nodeName = context.getNode().getNodeName(); 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 Fragments before parsing // 引入include 解析出的 sql 节点内容 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. // 处理 selectKey processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) 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)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }