XMLStatementBuilder
解析SQL节点,将定义的SQL节点信息构建成MappedStatement
对象。
1、MappedStatement
记录SQL节点信息,包含了很多属性,平时常见的属性有:
private String id;
private List<ResultMap> resultMaps;
private boolean useCache;
/**
* 记录缓存对象(二级缓存)
*/
private Cache cache;
/**
* sql片段集
*/
private SqlSource sqlSource;
private KeyGenerator keyGenerator;
除这些还有SqlCommandType、StatementType等等。
2、开启SQL节点解析
入口:parseStatementNode
。
一步一步看源码注释最清楚。
public void parseStatementNode() {
//NOTE. 节点id属性,对应Mapper接口中的方法名
String id = context.getStringAttribute("id");
//指定databaseId
String databaseId = context.getStringAttribute("databaseId");
//NOTE. 获取对应的SqlCommandType(select、insert、update、delete)
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//NOTE: 是否刷新缓存
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
//NOTE: 是否使用二级缓存(select 语句默认使用缓存)
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
//NOTE: <include>语句解析
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
//NOTE: 解析parameterType(如传入一个对象)
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
//NOTE:一般不会配置,使用默认的XMLLanguagerDriver
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
//NOTE: 解析<update/>等操作内部 的<SelectKey>内置节点
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
//NOTE: 定义MappedStatement的id,区别于其他SQL节点
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;
}
//NOTE: 创建SqlSource
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
//NOTE: Statement 默认PreparedStatement(有缓存)
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
//NOTE: resultType
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
//NOTE: resultMap
String resultMap = context.getStringAttribute("resultMap");
String resultSetType =
//NOTE: resultSetType,定义结果集的"滚动"模式
context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
//NOTE:update语句相关属性
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
//构建MappedStatement对象
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
2.1、XMLIncludeTransformer: <include/>节点解析
解析<include>节点,该过程会将其替换成<sql>节点中定义的SQL片段,并将其中的**”${xxx}“占位符替换为真实的参数**,主要过程在XMLIncludeTransformer.applyIncludes
方法内完成。TODO
2.2、创建 SqlSource:见SqlSource篇
见SqlSource篇
2.3、构建MappedStatement对象
- 创建MappedStatement,记录到Configuration的mappedStatements中
- key=currentNamespace+"."+id; value=MappedStatement。
//--☆☆--MapperBuilderAssistant
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
......
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean useCache,
......
LanguageDriver lang) {
//等着缓存配置解析完成(配置了cacheRef)
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//调用MappedStatement 内部静态类构建MappedStatement对象
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
//通过resultMap的名称、resultType解析出ResultMap对象
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
.......
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;
}
2.3.1、getStatementResultMaps
获得 ResultMap 集合。
private List<ResultMap> getStatementResultMaps(String resultMap, Class<?> resultType, String statementId) {
//NOTE: 补全resultMap的全限定名
resultMap = applyCurrentNamespace(resultMap, true);
List<ResultMap> resultMaps = new ArrayList<>();
//NOTE: 有配置resultMap属性,配置多个时,以','分开,并从configuration中找到对应的ResultMap
if (resultMap != null) {
String[] resultMapNames = resultMap.split(",");
for (String resultMapName : resultMapNames) {
try {
resultMaps.add(configuration.getResultMap(resultMapName.trim()));
} catch (IllegalArgumentException e) {
......
}
}
} else if (resultType != null) {
//配置resultType,构建“statementId + "-Inline"为id的ResultMap
ResultMap inlineResultMap = new ResultMap.Builder(configuration, statementId + "-Inline", resultType, new ArrayList<>(), null).build();
resultMaps.add(inlineResultMap);
}
return resultMaps;
}
2.3.2、MappedStatement.build()
校验配置有效性
assert mappedStatement.configuration != null;
assert mappedStatement.id != null;
assert mappedStatement.sqlSource != null;
assert mappedStatement.lang != null;
2.4、记录MappedStatement
记录MappedStatement到Configuration的mappedStatements Map中。
//记录MappedStatement到缓存中
configuration.addMappedStatement(statement);
//☆☆--Configuration: mappedStatements记录所有配置的MappedStatement
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
//☆☆--Configuration
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
private BiFunction<V, V, String> conflictMessageProducer;
public StrictMap(String name) {
super();
this.name = name;
}
/**
* Assign a function for producing a conflict error message when contains value with the same key.
* 若存在相同的key 时,返回错误信息
*/
public StrictMap<V> conflictMessageProducer(BiFunction<V, V, String> conflictMessageProducer) {
this.conflictMessageProducer = conflictMessageProducer;
return this;
}
@SuppressWarnings("unchecked")
public V put(String key, V value) {
//NOTE: 若key已经存在则会保错
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key
+ (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
}
//NOTE: 将key按照"."拆分,取数组最后一个string为key
if (key.contains(".")) {
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
//不存在则调用HashMap原生的方法进行保存
super.put(shortKey, value);
} else {
//NOTE: 若已经存在,则封装成Ambiguity再保存(存在的情况为:包路径不同,但Mapper接口名称一样): shortKey存在二义性
super.put(shortKey, (V) new Ambiguity(shortKey));
}
}
return super.put(key, value);
}
......
}