1. Overall functional architecture of MyBatis

Mybatis is mainly divided into three layers, from top to bottom: interface layer, data processing layer, and basic support layer. The architecture diagram is as follows:

2. Introduction of myBatis four core JDBC components

Mybatis built-in core 4 functional components, respectively are Executor, StatementHandler, ParameterHandler, ResultSetHandler, function description is as follows:

Component name Component description
Executor The framework’s core scheduler executor component, which is used for generating SQL statements and cache maintenance, has two implementations: BaseExecutor and CachingExecutor
StatementHandler To encapsulate JDBC statement operations, set parameters, and map results. The main implementation is BaseStatementHandler, CallableStatementHandler, PreparedStatementHandler, SimpleStatementHanlder, RoutingStatementHandl Er.
ParameterHandler This is used to map the parameters passed in to the parameters required by the statement
ResultSetHandler It is mainly used to map JDBC returned results to specific List collection, and complete the transformation from database type to program entity class. The default implementation is DefaultResultSethandler

3. Workflow diagram of MyBatis Mapper agent

Mybatis core class description

The name of the class Class description
SqlSessionFactoryBuilder SqlSessionFactory constructor, the builder pattern, uses the build method to build from InputStream, Reader, and so on
SqlSessionFactory SqlSession factory class, used to create SQLSessions, passing the Configuration parameter
SqlSession SqlSession code An SQL execution session that carries global configuration information
Configuration Mybatis global configuration management object, including all myBatis configuration information, including data source and all XML file information
MappedStatement And an insert | update | delete | select tags corresponding object, segments of a SQL statement used to store information
SqlSource It is used to generate final executable SQL statements for different types of SQL statements. The main implementations are DynamicSqlSource, RawSqlSource, StaticSqlSource, and MixedSqlSource
BoundSql It is used to bind and store SQL statement fragments and parameters, carrying SQL statement and parameter information
TypeHandler Used to complete the conversion between Java types and JDBC data types
XMLConfigBuilder It resolves global Configuration information, such as environment and dataSource information, and encapsulates the information into the Configuration
XMLMapperBuilder Is mainly used to parse the XML document in the select | insert | update | delete tags, and encapsulated into MappedStatement stored in the Configuration
XMLScriptBuilder It is mainly used to parse the dynamic tags and dynamic SQL statements in SQL statements. After parsing and parameter mapping, it finally constructs a StaticSqlSource to output and save a complete SQL statement that can be executed
XPathParser It is mainly used to parse configuration files and form Document objects that can be operated based on dom
ParameterMapping An object used to hold mappings between dynamic parameter names and parameter values in SQL

5, MyBatis configuration initialization part of the core source code

5.1 Analysis of program code entry:SqlSessionFactoryBuilder#build

The SqlSessionFactoryBuilder is: used to initialize the SqlSessionFactory, default DefaultSqlSessionFactory implementation

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // XMLConfigBuilder: used to parse XML configuration files
      // Use the Builder pattern
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // Parser.parse () : Parse the XML Configuration file using XPATH and encapsulate the Configuration file into a Configuration object
      // Return the DefaultSqlSessionFactory object, which has the Configuration object (encapsulating Configuration file information).
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.}}}public SqlSessionFactory build(Configuration config) {
    // Create a default implementation class for the SqlSessionFactory interface
    return new DefaultSqlSessionFactory(config);
}
Copy the code
5.2. Parse global Configuration information:XMLConfigBuilder#parse
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

/** * Parse the XML configuration file *@return* /
public Configuration parse(a) {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // Parser.evalNode ("/configuration") : Parse the configuration root node using the XPATH parser
    // Start parsing from the configuration root node and encapsulate the parsed content into the Configuration object
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

private void parseConfiguration(XNode root) {
    try {
        //issue #117 read properties first
        // Parse the  tag
        propertiesElement(root.evalNode("properties"));
        // Parse the 
       tag
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        // Parse the  tag
        typeAliasesElement(root.evalNode("typeAliases")); . . .// Parse the  tag
        environmentsElement(root.evalNode("environments"));
        // Parse the  tag
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        // Resolve the  tag
        typeHandlerElement(root.evalNode("typeHandlers"));
        // Parse the  tag
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: "+ e, e); }}Copy the code

6, mybatis mapper file load part of the core source code

**: **XMLConfigBuilder#mapperElement->XMLMapperBuilder#parse
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// Parse the mapper mapping file with XMLMapperBuilder
mapperParser.parse();

public void parse(a) {
    // Mapper Whether the mapping file has been loaded
    if(! configuration.isResourceLoaded(resource)) {// Start parsing from the 
      
        root tag in the mapping file until the complete parsing is complete
      
        configurationElement(parser.evalNode("/mapper"));
        // The tag is parsed
        configuration.addLoadedResource(resource);
        bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

/** * Parse the mapping file *@paramContext maps XNode */ corresponding to the root node of the file <mapper>
private void configurationElement(XNode context) {
    try {
        // Get the namespace value of the 
      
        tag
      
        String namespace = context.getStringAttribute("namespace"); . . .// Parse the 
      
        subtag
      
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        // Parse the 
      
        subtag
      
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        // Parse the < SQL > child tag, which is the SQL fragment
        sqlElement(context.evalNodes("/mapper/sql"));
        // Parse the 
        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
6.2 analytical MappedStatement: XMLMapperBuilder#buildStatementFromContext
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
        // MappedStatement parser
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            // Create an MappedStatement by parsing the four labels, including select
            statementParser.parseStatementNode();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        }
    }
}

MapperBuilderAssistant#addMappedStatement
    
// Use the Builder pattern to create the MappedStatement.Builder, which is used to create the MappedStatement object
MappedStatement.Builder statementBuilder = newMappedStatement.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) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, ! isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);if(statementParameterMap ! =null) {
    statementBuilder.parameterMap(statementParameterMap);
}

// Create an MappedStatement using mappedStatement. Builder
MappedStatement statement = statementBuilder.build();
// Store the MappedStatement object in the Map set in the Configuration. The key is the statement ID and the value is the MappedStatement object
configuration.addMappedStatement(statement);
Copy the code
6.3 processing SqlSource: XMLStatementBuilder#parseStatementNode->langDriver.createSqlSource
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
// Create SqlSource, parse SQL, encapsulate SQL statement (no parameter binding) and input parameter information
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

XMLLanguageDriver#createSqlSource
    
public SqlSource createSqlSource(Configuration configuration, XNode script, Class
        parameterType) {
    // The dynamic SQL tag handler is initialized
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    // Parse dynamic SQL
    return builder.parseScriptNode();
}

public SqlSource parseScriptNode(a) {
    // Parse the SQL statement in the select/INSERT/Update/delete tag, and finally encapsulate the parsed SqlNode into the List set in the MixedSqlNode
    // **** encapsulates the SQL information with the ${} number into TextSqlNode
    // **** encapsulate SQL information with #{} number to StaticTextSqlNode
    // **** Encapsulate the SQL information in the dynamic SQL tag into different SQLNodes
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource = null;
    // If SQL contains ${} and dynamic SQL statements, wrap SqlNode into DynamicSqlSource
    if (isDynamic) {
        sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
        // If SQL contains #{}, encapsulate SqlNode into RawSqlSource and specify parameterType
        sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
}
Copy the code