This is the 19th day of my participation in the August Wenwen Challenge.More challenges in August
Sequence diagram
sequenceDiagram
participant A as XMLMapperBuilder
participant B as ResultMapResolver
participant C as MapperBuilderAssistant
participant D as ResultMap.Builder
A ->> A : resultMapElements
A ->> A : resultMapElement
A ->> B : resolve
B ->> C : addResultMap
C ->> D : build
D -->> C : ResultMap
The detailed steps
XMLMapperBuilder#configurationElement
/** * Parse the mapping file's lower node *@paramContext mapping file root */
private void configurationElement(XNode context) {
try {
// Reads the namespace of the current mapping file
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
// Resolve other configuration nodes in the mapping file
// Parse the cache label
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
// Parse the parameter mapping
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// Parse the result set mapping
resultMapElements(context.evalNodes("/mapper/resultMap"));
// Parse SQL tags
sqlElement(context.evalNodes("/mapper/sql"));
// Process individual database operation statements
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); }}Copy the code
XMLMapperBuilder#resultMapElement
private void resultMapElements(List<XNode> list) throws Exception {
/ / traverse
for (XNode resultMapNode : list) {
try {
// Process a single resultMap label
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried}}}private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.emptyList(), null);
}
/** * compatible processing */
private ResultMap resultMapElement(XNode resultMapNode, List
additionalResultMappings, Class
enclosingType)
throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
/** * All types of resultMap related type tags can handle * For different types of resultMap tags, different attributes can be used to obtain type */
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
// Get typeClass through alias parsingClass<? > typeClass = resolveClass(type);// Get the inherited typeClass
if (typeClass == null) {
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<>();
// Put the parsed tags into the resultMappings
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
// Parse all sublabels under the resultMap label
for (XNode resultChild : resultChildren) {
// Parse the constructor tag
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
}
// Parse discriminator labels
else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
}
// Parse common tags
else {
List<ResultFlag> flags = new ArrayList<>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
// Build a ResultMapping object (if javaType is not specified, set javaType based on the property's set method parameter type and get the corresponding TypeHandler) and add it to the container
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
// Create a parser
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
/ / parsing
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throwe; }}Copy the code
ResultMapResolver#resolve
/** * The ResultMap inheritance relationship is resolved, and the result is placed in the Configuration resultMaps *@return* /
public ResultMap resolve(a) {
return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}
Copy the code
MapperBuilderAssistant#addResultMap
/** * Create the result mapping object *@paramThe id input parameter is based on the ResultMapResolver attribute *@return* / ResultMap object
public ResultMap addResultMap( String id, Class
type, String extend, Discriminator discriminator, List
resultMappings, Boolean autoMapping)
{
id = applyCurrentNamespace(id, false);
extend = applyCurrentNamespace(extend, true);
// Resolve the inheritance relationship of ResultMap
if(extend ! =null) {
// Check whether the ResultMap pointed to by extend exists in the Configuration object. If not, an exception is thrown and the current ResultMap is parsed again in the subsequent operations
if(! configuration.hasResultMap(extend)) {throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
// Obtain the parent ResultMap
ResultMap resultMap = configuration.getResultMap(extend);
// Get the parent attribute mapping
List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
// Remove attributes from the parent map that already exist in the subclass map to override the parent map with the subclass map
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
// If the current subclass ResultMap has a builder, the parent builder is removed
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break; }}if (declaresConstructor) {
extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
}
// All attribute mappings that are eventually inherited from the parent
resultMappings.addAll(extendedResultMappings);
}
// Create the current ResultMap
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
.discriminator(discriminator)
.build();
// Add the current ResultMap to the Configuration
configuration.addResultMap(resultMap);
return resultMap;
}
Copy the code
ResultMap#build
public ResultMap build(a) {
if (resultMap.id == null) {
throw new IllegalArgumentException("ResultMaps must have an id");
}
resultMap.mappedColumns = new HashSet<>();
resultMap.mappedProperties = new HashSet<>();
resultMap.idResultMappings = new ArrayList<>();
resultMap.constructorResultMappings = new ArrayList<>();
resultMap.propertyResultMappings = new ArrayList<>();
final List<String> constructorArgNames = new ArrayList<>();
for(ResultMapping resultMapping : resultMap.resultMappings) { resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() ! =null; resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() ! =null && resultMapping.getResultSet() == null);
final String column = resultMapping.getColumn();
if(column ! =null) {
// Add the current column to the mapped column, converting the name to all uppercase
resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
} else if (resultMapping.isCompositeResult()) {
for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
final String compositeColumn = compositeResultMapping.getColumn();
if(compositeColumn ! =null) { resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH)); }}}final String property = resultMapping.getProperty();
if(property ! =null) {
// Add the current attribute to the mapped attribute
resultMap.mappedProperties.add(property);
}
/ / if the mapping relationship is under the constructor label, is in the constructorResultMappings
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
resultMap.constructorResultMappings.add(resultMapping);
if(resultMapping.getProperty() ! =null) {
// If the name attribute is not empty in the mapping, put it in constructorArgNamesconstructorArgNames.add(resultMapping.getProperty()); }}else {
resultMap.propertyResultMappings.add(resultMapping);
}
// Put the IDS in the idResultMappings connection (ID tags and idArg tags)
if(resultMapping.getFlags().contains(ResultFlag.ID)) { resultMap.idResultMappings.add(resultMapping); }}// If the idResultMappings are empty, all the mappings are put in
if (resultMap.idResultMappings.isEmpty()) {
resultMap.idResultMappings.addAll(resultMap.resultMappings);
}
// When constructorArgNames are not empty
if(! constructorArgNames.isEmpty()) {// Get the parameter list in the corresponding constructor based on the configured parameter mapping
final List<String> actualArgNames = argNamesOfMatchingConstructor(constructorArgNames);
if (actualArgNames == null) {
throw new BuilderException("Error in result map '" + resultMap.id
+ "'. Failed to find a constructor in '"
+ resultMap.getType().getName() + "' by arg names " + constructorArgNames
+ ". There might be more info in debug log.");
}
/ / will be mapping constructorResultMappings order according to the obtained parameters
resultMap.constructorResultMappings.sort((o1, o2) -> {
int paramIdx1 = actualArgNames.indexOf(o1.getProperty());
int paramIdx2 = actualArgNames.indexOf(o2.getProperty());
return paramIdx1 - paramIdx2;
});
}
// lock down collections
// Lock the result set of the mapping
resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
return resultMap;
}
Copy the code
This is how Mybatis parses the resultMap tag.