1.2 启动过程中,Mapper.xml的解析
整体流程
简易流程图
graph LR
整体流程 --> 解析引用类缓存 --> 解析当前类缓存 --> 扫描器进行扫描 -->
解析入参映射关系 --> 解析出参映射关系 --> 解析SQL代码块 --> 解析SQL执行语句 --> 解析SQL节点 --> 结束
文本描述
- 启动时会初始化XMLMapperBuilder对象
- 内部构建XPathParser
- 进行解析
- 解析引用类缓存(cache-ref节点) cacheRefElement
- 解析当前类缓存(cache节点) cacheElement
- Configuration在构建过程中会在typeAliasRegistry的TYPE_ALIASES赋值
- 解析入参映射关系(/mapper/parameterMap 节点)parameterMapElement
- 解析出参映射关系(/mapper/resultMap 节点)resultMapElements
- 处理构造函数节点
- 构建ResultMapping属性
- 解析discriminator(鉴频器)
- 解析SQL代码块(/mapper/sql 节点) sqlElement
- 解析SQL执行语句() buildStatementFromContext 解析
- 解析SQL节点(select|insert|update|delete节点
- 解析SelectKeyNodes processSelectKeyNodes
父类接口 BaseBuilder
其他类解析:XPathParser
XML中的使用 XPathParser 进行解析
初始化
// 启动时通过此方法构建
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration,
resource,
sqlFragments);
}
// 核心初始化方法 设置父类的configuration、根据配置和资源构建builder助理、设置parser(解析器)、设置sql片段、设置资源
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
核心方法 parse(解析)
public void parse() {
// 判断资源是否被加载(判断configuration.loadedResources是否有该资源),如果没有加载,进行加载
if (!configuration.isResourceLoaded(resource)) {
// 解析 /mapper 元素 通过xpath解析节点
configurationElement(parser.evalNode("/mapper"));
// 将该资源记载到 configuration.loadedResources中,标记为已加载
configuration.addLoadedResource(resource);
/**
* 根据 Namespace 构建Mapper
* 1. 获取到解析出来的 Namespace 如果 Namespace 不为空 加载该接口
* 2. 判断 configuration.knownMappers 是否有该接口类
* 3. 如果没有在 configuration.loadedResources 中添加 "namespace:" + namespace 类,并将 configuration.knownMappers 添加 该接口类
*/
bindMapperForNamespace();
}
// 获取IncompleteResultMap中在解析过程中失败的ResultMap,再重新进行加载
parsePendingResultMaps();
// 获取IncompleteCacheRef中在解析过程中失败的CacheRef,再重新进行加载
parsePendingCacheRefs();
// 获取IncompleteStatement中在解析过程中失败的Statement,再重新进行加载
parsePendingStatements();
}
解析元素 configurationElement
如果Config中没有解析过该文件
private void configurationElement(XNode context) {
try {
// 在节点中获取namespace属性 获取到映射的mapper接口类
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 构建器中获取到映射属性
builderAssistant.setCurrentNamespace(namespace);
// 在mapper节点中 获取到cache-ref节点 用于配置引用类缓存
cacheRefElement(context.evalNode("cache-ref"));
// 在mapper节点中 获取cache节点 用于配置当前类缓存
cacheElement(context.evalNode("cache"));
// 在mapper节点中 获取 /mapper/parameterMap 节点 设置参数映射
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 在mapper节点中 获取 /mapper/resultMap 节点 设置返回结果
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 在mapper节点中 获取 /mapper/sql 节点
sqlElement(context.evalNodes("/mapper/sql"));
// 解析sql语句
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
解析引用类缓存(cache-ref节点) cacheRefElement
在当前mapper执行过程中,如果配置了CacheRef,那么CacheRef中配置的接口类,将在当前类使用过程中使用缓存,首先请确保CacheRef中配置的接口类已经使用了缓存
private void cacheRefElement(XNode context) {
// 如果节点不为空
if (context != null) {
// 配置中添加缓存信息(添加到cacheRefMap中,KEY为当前的Mapper接口全限定名,value为需要缓存的权限限定名)
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
// 构建CacheRefResolver
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
// 根据缓存的namespace获取缓存对象,并设置当前缓存currentCache
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
// 如果解析失败,存储到IncompleteCacheRef中,在处理完后加载
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
解析当前类缓存(cache节点) cacheElement
private void cacheElement(XNode context) throws Exception {
// 如果节点不为空
if (context != null) {
// 在节点中获取type属性,如果type属性为空,那么设置的默认值为:PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
// 根据缓存类型注册缓存获取缓存类
/**
* 在typeAliasRegistry.TYPE_ALIASES中根据类型获取
* 如果获取不到,通过Resources.classForName加载传入的类名
*/
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
// 在节点中获取eviction属性,如果eviction属性为空,name设置默认值为:LRU
String eviction = context.getStringAttribute("eviction", "LRU");
// 根据eviction类型加载eviction类
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
// 在节点中获取flushInterval属性
Long flushInterval = context.getLongAttribute("flushInterval");
// 在节点中获取size属性
Integer size = context.getIntAttribute("size");
// 在节点中获取是否只读标识,默认为false
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
// 在节点中获取是否阻塞标识,默认为false
boolean blocking = context.getBooleanAttribute("blocking", false);
// 在节点中获取子节点 构建成props对象
Properties props = context.getChildrenAsProperties();
// 生成代理器,使用缓存
/**
* 构建缓存对象:设置缓存类、缓存的回收策略、缓存刷新间隔、缓存大小、读写状态、阻塞状态、扩展属性
* 并在configuration中设置缓存对象
* 设置当前类的缓存对象
*/
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
Configuration在构建过程中会在typeAliasRegistry的TYPE_ALIASES赋值
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
缓存的回收策略
- LRU:最近最少使用,移除最长时间不被使用的对象
- FIFO:先进先出,按对象进入缓存的顺序来移除它们
- SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用,更积极地移除基于垃圾收集器和弱引用规则的对象
解析入参映射关系(/mapper/parameterMap 节点)parameterMapElement
private void parameterMapElement(List<XNode> list) throws Exception {
// 遍历parameterMap节点
for (XNode parameterMapNode : list) {
// 获取节点中的 ID 属性
String id = parameterMapNode.getStringAttribute("id");
// 获取节点中的 type 属性
String type = parameterMapNode.getStringAttribute("type");
// 加载 type类
Class<?> parameterClass = resolveClass(type);
// 在parameterMap节点中获取到 parameter节点
List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
// 声明参数参数的映射关系
List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
// 遍历parameter节点
for (XNode parameterNode : parameterNodes) {
// 在parameter节点中 获取 property、javaType、jdbcType、resultMap、mode、typeHandler、numericScale 属性
/**
* property 类中的属性名
* javaType java类型
* jdbcType 数据库类型
* resultMap 结果映射关系
* mode 标识参数类型:IN - 入参,OUT - 出参,INOUT - 入参和出参
* typeHandler 类型处理器
* numericScale 小数精度
*/
String property = parameterNode.getStringAttribute("property");
String javaType = parameterNode.getStringAttribute("javaType");
String jdbcType = parameterNode.getStringAttribute("jdbcType");
String resultMap = parameterNode.getStringAttribute("resultMap");
String mode = parameterNode.getStringAttribute("mode");
String typeHandler = parameterNode.getStringAttribute("typeHandler");
Integer numericScale = parameterNode.getIntAttribute("numericScale");
// 加载参数类型枚举
ParameterMode modeEnum = resolveParameterMode(mode);
// 加载java类型类
Class<?> javaTypeClass = resolveClass(javaType);
// 获取到jdbc类型枚举
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
// 加载 类型处理器
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
// 根据加载好的参数 构建参数映射
/**
* 1. 解析resultMap (拼接包名)
* 2. 解析javaTypeClass,如果javaType为空那么:判断JdbcType为CURSOR时加载java.sql.ResultSet类,如果返回结果为Map时加载object类,如果获取不到加载Object类型
* 3. 解析类型处理器
* 4. 构建ParameterMapping对象
*/
ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
// 将结果对象添加到parameterMappings中
parameterMappings.add(parameterMapping);
}
// 构建代理添加 configuration.parameterMaps对象中(KEY 为 id,value为构建好的ParameterMap对象)
builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
}
}
解析出参映射关系(/mapper/resultMap 节点)resultMapElements
遍历所有的resultMap节点 调用resultMapElement方法
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
// 在ThreadLocal中记录
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
// 在 节点中获取 id属性
String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
// 在 节点中获取 type类型,如果type为空获取ofType、如果ofType为空获取resultType、如果resultType为空获取javaType
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
// 在 节点中获取 extend属性
String extend = resultMapNode.getStringAttribute("extends");
// 在 节点中获取 autoMapping属性
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
// 加载resultMap的类
Class<?> typeClass = resolveClass(type);
// 声明 discriminator(鉴频器)
Discriminator discriminator = null;
// 声明 resultMappings
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
// 添加原有的resultMappings
resultMappings.addAll(additionalResultMappings);
// 在resultMap节点中获取子节点
List<XNode> resultChildren = resultMapNode.getChildren();
// 遍历所有的 resultMap节点
for (XNode resultChild : resultChildren) {
// 如果节点名为 构造函数 节点
if ("constructor".equals(resultChild.getName())) {
/**
* 1. 获取constructor节点的子节点
* 2. 添加flags中添加构造函数,如果节点名为idArg那么添加id
* 3. 构建ResultMapping 并将结果添加到resultMappings中
*/
processConstructorElement(resultChild, typeClass, resultMappings);
}
// 如果节点名为 discriminator(鉴频器)
else if ("discriminator".equals(resultChild.getName())) {
// 构建 discriminator(鉴频器)
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
// 判断节点名字是否为id 如果为ID 在标志中添加id字段
List<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
// 构建ResultMapping 并将结果添加到resultMappings中
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
// 根据上述属性构建 resultMapResolver
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
/**
* 1. 如果当前resultMap设置了集成属性,在configuration中获取继承的ResultMap,并取 继承的和本身的并集
* 2. 将组装好的ResultMap存储在configuration的resultMaps中 key 为 设置的ID value 为构建好的映射关系合集
*/
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
// 如果解析异常了,存放到IncompleteResultMap中,在处理结束后再次加载
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
处理构造函数节点
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
List<XNode> argChildren = resultChild.getChildren();
for (XNode argChild : argChildren) {
List<ResultFlag> flags = new ArrayList<ResultFlag>();
flags.add(ResultFlag.CONSTRUCTOR);
if ("idArg".equals(argChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
}
}
构建ResultMapping属性
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
String property;
// 如果存在构造函数标志 property为节点中的name属性 否则 为节点的property属性
if (flags.contains(ResultFlag.CONSTRUCTOR)) {
property = context.getStringAttribute("name");
} else {
property = context.getStringAttribute("property");
}
// 在节点中获取 column 列名
String column = context.getStringAttribute("column");
// 在节点中获取 javaType java类型
String javaType = context.getStringAttribute("javaType");
// 在节点中获取 jdbcType 数据库类型
String jdbcType = context.getStringAttribute("jdbcType");
// 在节点中获取 select 用于执行对应的select语句
String nestedSelect = context.getStringAttribute("select");
// 在节点中获取 resultMap
String nestedResultMap = context.getStringAttribute("resultMap",
processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
// 在节点中获取 notNullColumn 不为空的列 (如果该列为空,不生成子对象 - 待认证
String notNullColumn = context.getStringAttribute("notNullColumn");
// 在节点中获取 columnPrefix 当有多个结果集时,用该字段区分
String columnPrefix = context.getStringAttribute("columnPrefix");
// 在节点中获取 typeHandler 类型处理器
String typeHandler = context.getStringAttribute("typeHandler");
// 在节点中获取 resultSet 当有多个结果集时 获取设置的结果集
String resultSet = context.getStringAttribute("resultSet");
// 在节点中获取 foreignColumn 当有多个结果集时 映射字段
String foreignColumn = context.getStringAttribute("foreignColumn");
// 在节点中获取 fetchType 默认值为 在配置中获取的是否启动懒加载,如果启动返回TRUE 否则返回 FALSE
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
// 加载 java类
Class<?> javaTypeClass = resolveClass(javaType);
// 加载 类型处理器类
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
// 解析 jdbc类型
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
// 构建代理 根据配置 构建ResultMapping对象返回
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
解析discriminator(鉴频器)
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
// 在节点中获取到 column 列名
String column = context.getStringAttribute("column");
// 在节点中获取到 javaType java类型
String javaType = context.getStringAttribute("javaType");
// 在节点中获取到 jdbcType 数据库类型
String jdbcType = context.getStringAttribute("jdbcType");
// 在节点中获取到 typeHandler 类型处理器
String typeHandler = context.getStringAttribute("typeHandler");
// 解析java类
Class<?> javaTypeClass = resolveClass(javaType);
// 解析类型处理器
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
// 解析数据库类型
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
// 声明discriminator(鉴频器)Map
Map<String, String> discriminatorMap = new HashMap<String, String>();
// 遍历所有的子节点
for (XNode caseChild : context.getChildren()) {
// 获取子节点的 value 属性
String value = caseChild.getStringAttribute("value");
// 获取子节点的 resultMap 属性
String resultMap = ceChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings));
// 将value 和 resultMap 放置到discriminatorMap中
discriminatorMap.put(value, resultMap);
}
// 构建 discriminator(鉴频器)
return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
}
解析SQL代码块(/mapper/sql 节点) sqlElement
private void sqlElement(List<XNode> list) throws Exception {
// 如果 数据库配置了 DatabaseId(数据库厂商 方言)使用方言解析sql 额否则直接解析
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
}
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);
// 判断 databaseId (数据库方言) 是否匹配 如果匹配在sqlFragments添加记录 (同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃)
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
sqlFragments.put(id, context);
}
}
}
解析SQL执行语句() buildStatementFromContext 解析
private void buildStatementFromContext(List<XNode> list) {
// 如果 数据库配置了 DatabaseId(数据库厂商 方言)使用方言解析sql 额否则直接解析
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
// 解析 SQL 语句
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
// 遍历所有节点
for (XNode context : list) {
// 创建一个xml构建器
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 使用构建器解析
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
// 如果解析失败,添加到IncompleteStatement中,等当前文件解析完后重新构建
configuration.addIncompleteStatement(statementParser);
}
}
}
解析SQL节点(select|insert|update|delete节点)XMLStatementBuilder.parseStatementNode
使用XMLStatementBuilder解析
public void parseStatementNode() {
// sql节点中 获取 id
String id = context.getStringAttribute("id");
// sql节点中 获取 数据库方言
String databaseId = context.getStringAttribute("databaseId");
// 如果数据库方言不匹配直接返回
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// sql节点中 获取 fetchSize
Integer fetchSize = context.getIntAttribute("fetchSize");
// sql节点中 获取 timeout 超时时间
Integer timeout = context.getIntAttribute("timeout");
// sql节点中 获取 parameterMap 参数映射
String parameterMap = context.getStringAttribute("parameterMap");
// sql节点中 获取 parameterType 参数类型
String parameterType = context.getStringAttribute("parameterType");
// 解析参数类型
Class<?> parameterTypeClass = resolveClass(parameterType);
// sql节点中 获取 resultMap 结果映射
String resultMap = context.getStringAttribute("resultMap");
// sql节点中 获取 resultType 结果类型
String resultType = context.getStringAttribute("resultType");
// sql节点中 获取 lang 为特定的语句指定语言(驱动类)?
String lang = context.getStringAttribute("lang");
// 解析语言驱动
LanguageDriver langDriver = getLanguageDriver(lang);
// 解析结果类型
Class<?> resultTypeClass = resolveClass(resultType);
// sql节点中 获取 resultSetType 指定的结果类型
String resultSetType = context.getStringAttribute("resultSetType");
// sql节点中 获取并解析 statementType 语句类型
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
// 解析 结果类型
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
// 获取节点的名字
String nodeName = context.getNode().getNodeName();
// 获取节点的SQL语句类型
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
// 如果为查询语句,那么设置标记
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// sql节点中 获取 flushCache 是否刷新缓存,默认不是查询都会开启
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
// sql节点中 获取 useCache 是否使用缓存,默认是查询都会开启
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
// sql节点中 获取 resultOrdered 是否返回多个结果集,默认是不开启
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// 解析 include 节点
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// 解析 SelectKey 节点
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
// langDriver(语言驱动)创建SQL原数据 (设置不同节点的节点处理器)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
// sql节点中 获取 resultSets 返回结果的集合名字
String resultSets = context.getStringAttribute("resultSets");
// sql节点中 获取 keyProperty 返回后设置的属性名
String keyProperty = context.getStringAttribute("keyProperty");
// sql节点中 获取 keyColumn 返回后设置的属性的列
String keyColumn = context.getStringAttribute("keyColumn");
// 声明Key生成器
KeyGenerator keyGenerator;
// 声明 keyStatementId ? 为当前SQL语句的ID + !selectKey
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
// 构建当前类的ID
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
// 如果配置有ID生成器 通过ID生成器获取keyGenerator 否则 在节点中获取 useGeneratedKeys 使用的KEY生成器
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
// 构建MappedStatement 构建 MappedStatement 存放在 Configuration.ji sql查询的id
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
解析SelectKeyNodes processSelectKeyNodes
private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
// sql节点中 获取 selectKey 节点
List<XNode> selectKeyNodes = context.evalNodes("selectKey");
// 如果 数据库配置了 DatabaseId(数据库厂商 方言)使用方言解析 额否则直接解析
if (configuration.getDatabaseId() != null) {
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
}
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
removeSelectKeyNodes(selectKeyNodes);
}
private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
// 遍历获取到的节点
for (XNode nodeToHandle : list) {
// 设置id (由SQL语句的ID + !selectKey 组成)
String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
// 获取数据库驱动
String databaseId = nodeToHandle.getStringAttribute("databaseId");
// 如果驱动一致
if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
}
}
}
private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
// 在节点中 获取 resultType 结果类型
String resultType = nodeToHandle.getStringAttribute("resultType");
// 解析结果类型
Class<?> resultTypeClass = resolveClass(resultType);
// sql节点中 获取并解析 statementType 语句类型
StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
// sql节点中 获取 keyProperty 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称
String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
// 在节点中 获取 keyColumn 返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称
String keyColumn = nodeToHandle.getStringAttribute("keyColumn");
// 在节点中 获取 order 设置selectKey的执行属性 在执行语句之前,还是之后
boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));
// 设置默认值 不适用缓存、没有多个结果集、没有Key生成器、返回大小为空、不设置超时时间、不刷新缓存、入参映射为空、出参映射为空、返回结果为空
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;
// langDriver(语言驱动)创建SQL原数据 (设置不同节点的节点处理器)
SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
// SQL执行类型为SELECT(查询)
SqlCommandType sqlCommandType = SqlCommandType.SELECT;
// 构建MappedStatement 构建 MappedStatement 存放在 Configuration.mappedStatements KEY为SelectKey的id
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);
// 并将当前SQL存在 configuration.keyGenerators 中 key为SQL语句的ID
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
}