【MyBatis Detailed】——From Xml configuration analysis to SQL execution process

table of Contents

Overall structure and use of Demo

The overall architecture of Mybatis can be divided into 3 layers:

Insert picture description here
  1. The interface layer
    is to interact with the database. The core interface is SqlSession. A SqlSession corresponds to a database session, so its life cycle is not permanent. In theory, it needs to be created every time the database is accessed.
    There are two forms, one is to use the Mapper interface, and the other is based on the Api provided by Mybatis;
  • The Api provided by Mybatis
    requires us to provide StatementId and query parameters, pass them to the SqlSession object, and provide the SqlSession object to interact with the database; but this form of creating sqlSession does not conform to the habit of interface-oriented programming.
  • Use Mapper interface:
    Mybatis abstracts each Mapper node in the configuration file as a Mapper interface. According to SqlSession.getMapper(XXXMapper.class), Mybatis will generate a Mapper instance through dynamic proxy. But when we call the method in the Mapper interface, Mybatis will determine the StatementId according to the method name and parameters, and the bottom layer is to implement the operation of the database through SqlSession.
  1. Data processing layer
  • Configuration analysis
  • Parameter and result set mapping: conversion between java data type and jdbc data type, including query phase and result return phase
  • Sql analysis: dynamic sql generation
  • Sql execution
  1. Framework support layer
  • Transaction management
  • Connection pool management
  • Caching mechanism

Use Demo:

String resource = "mybatis-config.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource); 
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 
// 创建SqlSession 
SqlSession sqlSession = sqlSessionFactory.openSession(); 
// 执行SQL语句 (通过StatementId)
List list = sqlSession.selectList("com.example.mapper.personMapper.selectPersonByMap");
// 执行SQL语句(通过Mapper接口)
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
personMapper.selectPersonByMap();

There are two forms of initialization of Mybatis:

  • Based on XML configuration file: Through the XML configuration file, the configuration information is parsed into Configuration objects.
  • Based on Java API: Manually create the Configuration object in the Java code, and then set the configuration parameters into the Configuration object. (Not recommended)

Mybatis core process

1. Create SqlSessionFactory

String resource = "mybatis-config.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource); 
// 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 
Insert picture description here

Parse the xml configuration information into a Configuration object, and then build a factory of the DefaultSqlSessionFactory type:

  1. Create XMLConfigBuilder, parse the xml global file
  2. Create XMLMapperBuilder, parse each xml
  3. Finally, the Configuration object will be returned, saving all the xml configuration information;

Configuration analysis-SQL analysis (store MappedStatement to Configuration)

We focus on analyzing the parse process of sql related information in the XMLMapperBuilder.parse method:

Insert picture description here
  • Parse the Mapper node to obtain the XNode, then parse the select|insert|update|delete node in the XNode, and save it as a List of type XNode; traverse the XNode node of CRUD:
  • Create a new XMLStatementBuilder and call parseStatementNode to parse each CRUD node
  • Create LanguageDriver (the default is XMLLanguageDriver, used to process the sql part in xml)
  • Call LanguageDriver.createSqlSource to get SqlSource
  • Create MappedStatement based on SqlSource and register it in Configuration

CreateSqlSource method to obtain SqlSource:

Insert picture description here
  1. Create a new XMLScriptBuilder, and call parse to parse the sql part of each node
  2. Recursive analysis, and finally MixedSqlNode (essentially a List collection of SqlNode), and mark this Sql type as dynamic or static
  3. If it is a dynamic type, return DynamicSqlSource
  4. If it is static type, return RawSqlSource
  5. The returned SqlSource will be saved in the MappedStatement, and then when the sql is executed, the sql parsing is triggered through the getBoundSql method

to sum up

  • XMLConfigBuilder
    parses the overall xml file
  • XMLMapperBuilder
    parses each xml file
  • XMLStatementBuilder
    parses the select, insert, update, and delete nodes in the xml file
  • XMLScriptBuilder
    parses the Builder of each node sql part in xml, generates MapperStatement, and saves it in Configuration

SqlNode

The tool class for node parsing is simply understood as each tag in xml, such as update, trim, if tag.
The implementation class includes

Insert picture description here


the key to realize dynamic Sql is the apply method of each SqlNode.
Take IfSqlNode as an example, if the conditions are met, apply And return true

 @Override
  public boolean apply(DynamicContext context) {
    if (evaluator.evaluateBoolean(test, context.getBindings())) {
      contents.apply(context);
      return true;
    }
    return false;
  }

And StaticTextSqlNode type static sql, directly append

@Override
  public boolean apply(DynamicContext context) {
    context.appendSql(text);
    return true;
  }

MappedStatement

It is an attribute in Configuration, which represents the information of a CRUD node

SqlSource

Is the attribute of MappedStatement, the implementation class includes:

Insert picture description here
  • StaticSqlSource : The final static SQL statement package, other types of SqlSource are ultimately entrusted to StaticSqlSource.
  • RawSqlSource : The encapsulation of the original static SQL statement. The SQL statement is already determined at the time of loading, which is faster than the dynamic SQL statement because it does not need to parse the SQL node at runtime.
  • Such as # placeholder
  • DynamicSqlSource : Encapsulation of dynamic SQL statements. It is necessary to process tags such as if or ${} SQL splicing according to parameters at runtime to generate the final static SQL statement to be executed.
  • Tags such as $placeholder or if
  • ProviderSqlSource : When the SQL statement is obtained through the specified class and method (using the @XXXProvider annotation), you need to use this class, and it will call the corresponding method to get the SQL statement through reflection.

Its getBoundSql method provides a BoundSql object.

BoundSql

It is the information obtained from SqlSource, which encapsulates the sql class that mybatis finally generates, including sql statements, parameters, parameter source data and other parameters; BoundSql will be used when Sql is executed later.

Insert picture description here

2. Create SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession(); 
Insert picture description here
  1. Create Transaction:
  2. Obtain Environment object according to Configuration configuration information
  3. Obtain TransactionFactory according to the environment information, the transaction factory obtained here corresponds to the transactionManager node in the xml file
  4. newTransaction creates transaction tx
  5. Create an Executor executor :
  6. Call the newExecutor method of the Configuration object and pass the transaction object tx into it;
  7. The newExecutor method creates a SimpleExecutor of the Simple type by default
    (there are three types: SIMPLE, REUSE, BATCH).
    If cacheEnabled (enabled by default) is turned on, it is packaged as CachingExecutor: first look up the cache before querying the data, and then download it from the database if it is not found Query and join the cache.
  8. executor = (Executor) interceptorChain.pluginAll(executor);
    uses the executor chain mode, so that the Executor object can be intercepted by the plug-in;
  9. Create DefaultSqlSession:
  10. new DefaultSqlSession(configuration, executor, autoCommit)

SqlSession

The core interface of Mybatis interacting with the database, a SqlSession corresponds to a database session.

Executor

Executor, the real SQL execution is not executed directly by SqlSession, but executed by Executor.

3. Get Mapper

PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Insert picture description here
  1. Obtain the target MapperProxyFactory through MapperRegister (Map<Class<?>, MapperProxyFactory<?>> of Map type) according to the interface Class information
  2. Create a MapperProxy (that is, a proxy for the Mapper interface) through the MapperProxyFactory
  3. Proxy.newProxyInstance dynamic proxy creation
  4. So far, the personMapper object we obtained through SqlSession.getMapper is actually a proxy class

4. Call the CRUD method through the Mapper interface

personMapper.selectPersonByMap();
Insert picture description here

Access to the methods of the proxy object will be implemented on the proxy's invoke, so the called mapper.select and other methods will pass through the invoke in the MapperProxy:

  1. Create MapperMethod object:
    Distribute calls to different methods of SqlSession according to the type of CRUD
  2. Processing parameters:
    convertArgsToSqlCommandParam: when the number of args>1, wrap the parameters of Object[] as Map<String, Object>
  3. Execution of the SqlSession method:
  4. According to the statementId stored in the internal class SqlCommand of MapperMethod, find the corresponding MappedStatement from the Configuration
  5. Take MappedStatement as a parameter and call the corresponding method in Executor

MapperMethod

It is a distributor that distributes calls to different methods of SqlSession according to the type of CRUD.
Two internal classes are created at the same time:
-SqlCommand
interfaceName + methodName as statementId, go to Configuration to find the corresponding MappedStatement to
store statementId, SqlCommandType (CRUD type) information
-MethodSignature
records method parameters, return values ​​and other information

5. Executor executes SQL

Insert picture description here
  1. We call the cached Executor by default, then first query the cache locally, if not found, then call the query method of SimpleExecutor (default simple type)
  2. Create StatementHandler : The
    default type is ParparedStatementHandler, Executor will perform tasks to it, it is the specific executor of sql. It can also be blocked by plug-ins (common physical paging plug-ins)
  3. prepare: pre-compile sql and get the Statement object
  4. Call ParameterHandler: process parameters
  5. Call ResultSetHandler: process the execution result

StatementHandler

It can be seen that the Executor is not the real executor, but is completed by the StatementHandler. In the process of StatementHandler being created, there is a getBoundSql step, which triggers the processing of dynamic SQL, which will be analyzed in detail in the next section.