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.