文章目录

作用

XMLStatementBuilder作用是解析select、update、insert、delete标签里的SQL语句的。

构造方法

XMLStatementBuilder 继承了BaseBuilder,有如下两个构造方法,用到的也就是第二个了。

mybatis配置加载阶段源码之XMLStatementBuilder_include标签

public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
super(configuration);
this.builderAssistant = builderAssistant;
this.context = context;
this.requiredDatabaseId = databaseId;
}

XMLStatementBuilder的对象的生成是在XMLMapperBuilder#buildStatementFromContext方法中生成的,也是目前唯一的一个生成地方

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}

方法

XMLMapperBuilder 核心的方法是parseStatementNode方法,解析也是从parseStatementNode方法开始的。

parseStatementNode

先看下parseStatementNode方法源码,这个方法有点长

public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
/**
* 判断databaseId 是否能匹配到当前databaseId和
* id对应的SQL是否已经加载过
*/
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}

String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
// 是否是select标签
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
// 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

/**
* 替换Include标签中的占位符,然后移除include标签
*/
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());

//参数类型
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);

// 获取语言驱动
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);

/**
* 解析并移除selectKey节点
*/
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);

/**
* 解析SQL,前提是<selectKey> 和 <include> 节点已经被解析和移除了
*/
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
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;
}

/**
* XMLScriptBuilder#parseScriptNode
* 获取DynamicSqlSource或者RawSqlSource
*
*/
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));

Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

步骤:

1、根据标签id判断当前标签是否加载过,是否生成过MappedStatement

2、判断select标签是否使用过缓存等并获取缓存等对应的值。

3、解析include标签和SQL标签等

3.1 解析include标签,这一部分在XMLIncludeTransformer#applyIncludes方法中,解析include标签时会递归调用applyIncludes方法把include标签的子标签property中的name和value取出来放到Properties对象variablesContext中以便在解析sql等元素标签时替换掉变量。

3.2 解析sql标签等,在SQL等的标签中会替换标签中用’${}'包裹的变量,替换变量的方法在PropertyParser#parse方法中,最终替换操作在GenericTokenParser#parse方法中。

4、解析key生成器和selectkey标签,获取key的生成策略。

5、获取SqlSourceSource对象

6、获取select、update、insert、delete标签 的statementType、resultMap、resultType、keyProperty等的各项属性

7、生成MappedStatement对象并返回,MappedStatement包含了一个标签的所有信息。

以上就是一个select、update、insert、delete标签的解析过程。

能力有限,水平一般,如有错误,请多指出。