回到 XMLStatementBuilder.processSelectKeyNodes 的方法
private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
// 拿到所有 selectKey 节点
List<XNode> selectKeyNodes = context.evalNodes("selectKey");
if (configuration.getDatabaseId() != null) {
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
}
// 解析selectKey节点
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
// 遍历并移除selectKey节点,防止干扰主节点后续解析流程
removeSelectKeyNodes(selectKeyNodes);
}
/**
parentId 就是上级节点id,其实就是标签的id;
parameterTypeClass 也是标签上设置的参数类型
langDriver 就是 XMLLanguageDriver
skRequiredDatabaseId 一般情况下为 null
*/
private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
for (XNode nodeToHandle : list) {
// 给selectKey 语句生成一个以“!selectKey”结尾的id
String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
// databaseId, skRequiredDatabaseId 这两个值一般为null,实际生产也基本为null
String databaseId = nodeToHandle.getStringAttribute("databaseId");
if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
// 两种情况会走到这里:1.如果configuration 中没有当前 id 的statement;2.如果有 statement,但是databaseId 为 null
parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
}
}
}
private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
// 下面这一段都是获取开发时对selectKey配置的几个属性,各属性的值前面也有介绍,或者自己去官网捞出来看
String resultType = nodeToHandle.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
String keyColumn = nodeToHandle.getStringAttribute("keyColumn");
boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));
//defaults
boolean useCache = false;
boolean resultOrdered = false;
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
Integer fetchSize = null;
Integer timeout = null;
boolean flushCache = false;
String parameterMap = null;
String resultMap = null;
ResultSetType resultSetTypeEnum = null;
// 创建 sqlSource,看章节`3.2.2.6`
SqlSource sqlSource = .createSqlSource(configuration, nodeToHandle, parameterTypeClass);
SqlCommandType sqlCommandType = SqlCommandType.SELECT;
// 创建 MappedStatement 并添加到 configuration 中,看章节`3.2.2.6`
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);
// 再从 configuration 中把 刚创建的 MappedStatement 对象取出来
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
// 生成 SelectKeyGenerator 并存放到 configuration 的相应 HashMap 中
configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
}
private void removeSelectKeyNodes(List<XNode> selectKeyNodes) {
for (XNode nodeToHandle : selectKeyNodes) {
// 遍历并移除selectKey节点
nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
}
}
现在可以回答本章刚开始的几个问题了。
1、谁来解析?
答:由 XMLMapperBuilder类来解析
2、用什么解析?
答:内部通过XPath来解析xml
3、解析成什么?
答:解析成 SqlSource
4、解析结果如何存放?
答:最终通过 MappedStatement 来包装,并存放在Configuration中
5、最终用途?
答:待执行器执行sql时使用
后续将通过抖音视频/直播的形式分享技术,由于前期要做一些准备和规划,预计2024年6月开始,欢迎关注,如有需要或问题咨询,也可直接抖音沟通交流。