I met MyBatis

MyBatis is the first class persistence framework to support custom SQL, stored procedures, and advanced mapping. MyBatis eliminates much of JDBC boilerplate code, manual parameter Settings, and retrieval results. MyBatis supports simple XML and annotation configuration rules. Enable Map interfaces and POJO classes to Map database fields and records.

The characteristics of MyBatis

So what are the characteristics of MyBatis? Perhaps we can describe it from the following aspects

  • The SQL statements in MyBatis are separated from the main business codes. We generally put the SQL statements in MyBatis in the XML configuration file for unified maintenance.

  • Decouple SQL from program code and provide DAO layer to separate business logic and data access logic, making system design clearer, easier to maintain and easier to unit test. Separation of SQL and code improves maintainability.

  • MyBatis is relatively simple and lightweight

It’s small and simple in itself. There are no third-party dependencies, just by configuring the JAR package, or by configuring Maven if you are using Maven projects. Easy to use, through the documentation and source code, you can fully grasp its design ideas and implementation.

  • Masked boilerplate code

MyBatis removes the original JDBC boilerplate code, allowing you to focus more on SQL writing and property-field mapping.

  • Write native SQL to support multiple table association

The most important feature of MyBatis is that you can write SQL statements manually and can support multiple table associated query.

  • Provides mapping labels to support ORM field relational mapping of objects to databases

What is ORM? Object Relational Mapping (ORM) is the automatic persistence of objects in object-oriented language programs into Relational databases by using metadata that describes mappings between objects and databases. It’s essentially converting data from one form to another.

  • Provides XML tags to support writing dynamic SQL.

You can use MyBatis XML tag, play the SQL template effect, reduce the complex SQL statement, easy to maintain.

MyBatis overall architecture

MyBatis is on top of the interface layer, the interface layer is the developer in Mapper or Dao interface definition, is query, add, update or delete operations; The middle layer is the data processing layer, which is mainly the process of configuring parameter mapping, SQL parsing, SQL execution and result mapping between Mapper -> XML levels. Both processes are supported by basic support layers, including connection management, transaction management, configuration loading, cache handling, and so on.

The interface layer

Without integration with Spring, MyBatis performs database operations as follows:

InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
sqlSession = factory.openSession();
Copy the code

SqlSessionFactory, SqlSession is the core of the MyBatis interface classes, especially the SqlSession, the interface is the most important thing in MyBatis interface, this interface allows you to execute the command, mapping, management affairs.

Data processing layer

  • Configure the parsing

During the initialization of Mybatis, the Mybatis -config. XML configuration file, mapping configuration file, and annotation information in Mapper interface will be loaded. The parsed configuration information will be formed into an object and stored in the Configration object. The SqlSessionFactory object is then created from that object. After Mybatis initialization is complete, you can use SqlSessionFactory to create SqlSession objects and start database operations.

  • SQL parsing and scripting modules

Mybatis dynamic SQL statement, almost all can be written to meet the needs of THE SQL.

The Scripting module in Mybatis parses the dynamic SQL nodes defined in the mapping file according to the parameters passed in by the user and forms SQL statements that can be executed by the database.

  • SQL execution

SQL statement execution involves multiple components, including the four cores of MyBatis: Executor, StatementHandler, ParameterHandler, ResultSetHandler. The SQL execution process can be represented in the following diagram

MyBatis hierarchy of the various components of the introduction (here is just a brief introduction, detailed in the following) :

  • SqlSession:, it is the core API of MyBatis, mainly used to execute commands, obtain mappings, and manage transactions. Receive the Statement Id and parameters from the developer. And return the operation result.
  • Executor: executer, is the core of MyBatis scheduling, responsible for the generation of SQL statements and the maintenance of query cache.
  • StatementHandler: encapsulates the JDBC Statement operation and performs operations on the JDBC Statement, such as setting parameters and converting the Statement result set to a List.
  • ParameterHandler: Is responsible for converting the parameters passed by the user into the parameters required by the JDBC Statement.
  • ResultSetHandler: Converts the ResultSet object returned by JDBC into a collection of type List.
  • TypeHandler: for converting between Java and JDBC types.
  • MappedStatement: Dynamic SQL encapsulation
  • SqlSource: represents the content of a mapping statement read from an XML file or comment, which creates THE SQL that passes input parameters received from the user to the database.
  • Configuration: All the Configuration information of MyBatis is kept in the Configuration object.

Base support layer

  • Reflection module

Reflection module in Mybatis, has a good encapsulation of Java reflection, provides a simple API, convenient upper-layer call, and a series of optimization of reflection operations, such as caching MetaClass and MetaObject metadata, Improved performance of reflection operation.

  • Type conversion module

Alias mechanism of Mybatis, which can simplify configuration files, is one of the main functions of type conversion module. Another function of the type conversion module is to convert JDBC types to Java types. When SQL statements bind parameters, the data is converted from Java type to JDBC type. When the result set is mapped, the data is converted from the JDBC type to the Java type.

  • The log module

In Java, there are many excellent logging frameworks, such as Log4j, Log4j2, SLF4J, etc. In addition to providing detailed log output information, Mybatis can integrate multiple log frameworks. The main function of its log module is to integrate third-party log frameworks.

  • Resource loading module

This module mainly encapsulates the class loader, determines the use sequence of the class loader, and provides the function of loading class files and other resource files.

  • Parser module

This module has two main functions: one is to encapsulate XPath and provide support for parsing Mybatis -config. XML configuration file and mapping configuration file during initialization of Mybatis; The other provides support for handling placeholders in dynamic SQL statements.

  • Data source module

Mybatis itself provides the corresponding data source implementation, and also provides the interface to integrate with the third-party data source. Data source is one of the common components in development. Many open source data sources provide rich functions, such as connection pooling, connection status detection, etc. Selecting data source components with excellent performance is very important for providing ORM framework and the performance of the whole application.

  • Transaction management module

Generally, Mybatis integrates with the Spring framework, which manages transactions. However, Mybatis abstracts the database transaction itself, and provides the corresponding transaction interface and simple implementation.

  • The cache module

Mybatis has level 1 cache and level 2 cache, both of which depend on the implementation in the cache module. However, it should be noted that these two levels of cache, Mybatis and the entire application run in the same JVM and share the same memory. If the amount of data in these two levels is large, other functions in the system may be affected. Therefore, if a large amount of data needs to be cached, the use of cache products such as Redis and Memcache is preferred.

  • The Binding module

SQL nodes defined in the mapping file need to be specified when the corresponding SqlSession method is called to perform database operations. If there is a spelling error in the SQL, it can only be detected at run time. In order to detect such errors as soon as possible, Mybatis associates the user-defined Mapper interface with the mapping file through the Binding module, and the system can execute the corresponding SQL statement to complete the database operation by calling the methods in the user-defined Mapper interface, thus avoiding the above problems. Note that in development, we only created the Mapper interface and did not write the implementation class, because Mybatis automatically created dynamic proxy objects for the Mapper interface.

MyBatis core components

Now that you know MyBatis and understand its infrastructure, let’s take a look at the core components of MyBatis, which transform from SQL statements to mapping to JDBC to database fields, execute SQL statements, and output result sets. Let’s start with the first core component of MyBatis

SqlSessionFactory

As with any framework, you have to go through a series of initialization processes before using the framework, and MyBatis is no exception. The initialization process of MyBatis is as follows

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSessionFactory.openSession();
Copy the code

SqlSessionFactory is an interface in MyBatis framework. SqlSessionFactory is an interface in MyBatis framework. SqlSessionFactory is an interface in MyBatis framework

  • MyBatis framework initialization operation
  • For developersSqlSessionobject

SqlSessionFactory has two implementation classes, one is SqlSessionManager class, one is DefaultSqlSessionFactory class

  • DefaultSqlSessionFactory: The default implementation class for SqlSessionFactory, which is a factory class that actually produces sessions. Instances of this class have a global lifecycle, generating only one instance (singleton mode) on the first invocation and remaining until the server is shut down.

  • SqlSessionManager: deprecated SqlSessionManager needs to maintain its own thread pool. MyBatis is more about integrating with Spring than using it alone, so it doesn’t make sense to maintain your own ThreadLocal. So SqlSessionManager is no longer in use.

####SqlSessionFactory execution process

The following is an analysis of the SqlSessionFactory execution process

The first step is to create the SqlSessionFactory

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
Copy the code

Starting with this line of code, we first create an SqlSessionFactoryBuilder factory. This is a builder pattern design idea. The Builder creates the SqlSessionFactory factory

Then call the build method in SqlSessionFactoryBuilder and pass in an InputStream. The InputStream is the mybatis-config.xml configuration file that you pass in. SqlSessionFactoryBuilder creates an XMLConfigBuilder object from the incoming InputStream InputStream and the environment and properties properties. The SqlSessionFactoryBuilder object calls the parse() method of XMLConfigBuilder as follows.

XMLConfigBuilder parses the/Configuration tag. Configuration is the most important tag in MyBatis. The following flow describes the Configuration tag.

MyBatis default use XPath to parse the labels, on the use of XPath, see www.w3school.com.cn/xpath/index…

In the parseConfiguration method, each label in/Configuration is parsed

Important configuration

Can you tell me what these labels mean

  • propertiesExternal properties, which are externally configurable and dynamically replaceable, either in a typical Java properties file or passed through child elements of the Properties element.
<properties>
    <property name="driver" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="root" />
</properties>
Copy the code

It is used to assign a value to the dataSource in the environment tag

<environment id="development">
  <transactionManager type="JDBC" />
  <dataSource type="POOLED">
    <property name="driver" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
  </dataSource>
</environment>
Copy the code

You can also configure it through external properties, but this article will focus on principles and won’t cover much application-level manipulation.

  • settings, which will change the runtime behavior of MyBatis.

There are many Settings in the Settings. For details, see mybatis.org/mybatis-3/z… Understand in detail. Here are some important configurations in common use

attribute describe
cacheEnabled Globally turn on or off any caches that have been configured by all mappers in the configuration file.
useGeneratedKeys Allows JDBC support for automatic primary key generation, requiring driver support. If set to true, this setting enforces the use of auto-generated primary keys.
lazyLoadingEnabled Global switch of lazy loading. When enabled, all associated objects are lazily loaded.
jdbcTypeForNull Specifies the JDBC type for null values when no specific JDBC type is provided for the parameter. Some drivers need to specify the JDBC type of the column, and in most cases just use the generic type, such as NULL, VARCHAR, or OTHER.
defaultExecutorType Configure the default actuator. SIMPLE is a plain actuator; The REUSE executor reuses prepared statements. The BATCH executor reuses statements and performs BATCH updates.
localCacheScope MyBatis uses the Local Cache mechanism to prevent circular references and speed up repeated nested queries. The default value is SESSION, in which case all queries executed in a SESSION are cached. If the value is set to STATEMENT, the local session is only used for STATEMENT execution. Different calls to the same SqlSession will not share data
proxyFactory Specifies the proxy tool Mybatis uses to create lazy-loading objects.
mapUnderscoreToCamelCase Whether to enable Automatic Camel Case mapping from the classic database column name A_COLUMN to the classic Java property name aColumn.

Generally, the following configuration is used

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
</settings>
Copy the code
  • typeAliasesA type alias is a name set for a Java type. It is only about XML configuration.
<typeAliases>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>
Copy the code

When configured this way, blogs can be used anywhere doma.blog.blog is used.

  • typeHandlers, type processor, both MyBatis inPrepare statement (PreparedStatement)When you set a parameter, or when you extract a value from the result set, the type handler converts the value to the Java type in the appropriate way.

There are a number of typeHandlers already implemented under the org.apache.ibatis. Type package, which you can refer to below

You can override the type handler or create your own type handler to handle unsupported or non-standard types.

Specific measures are as follows: Implement org. Apache. Ibatis. Type. TypeHandler interface, very convenient or inherit a class org. Apache. Ibatis. The BaseTypeHandler, which can then be selectively map it to a JDBC type.

  • objectFactoryEach time MyBatis creates a new instance of the resulting object, it uses an ObjectFactory instance to do so. All the default object factory needs to do is instantiate the target class, either through the default constructor or, if the parameter mapping exists, through the parameter constructor. If you want to override the default behavior of an object factory, you can do so by creating your own object factory.
public class ExampleObjectFactory extends DefaultObjectFactory {
  public Object create(Class type) {
    return super.create(type);
  }
  public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }
  public <T> boolean isCollection(Class<T> type) {
    returnCollection.class.isAssignableFrom(type); }}Copy the code

You then need to configure this object factory in the XML

<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>
Copy the code
  • Plugins are interfaces that MyBatis designers give developers to develop themselves. MyBatis allows you to intercept calls at certain points during the execution of mapped statements. MyBatis allows you to intercept method calls using plug-ins: The Executor, ParameterHandler, ResultSetHandler, and StatementHandler interfaces are also important interfaces in MyBatis. We will discuss these interfaces in detail below.

  • MyBatis can be configured to accommodate a variety of environments. This mechanism helps to apply SQL mapping to a variety of databases. For example, development, test, and production environments require different configurations; Or you want to use the same SQL mapping across multiple production databases with the same Schema.

    Note that while environments can specify multiple environments, SqlSessionFactory can only have one. To specify which environment to create, simply pass it as an optional parameter to SqlSessionFactoryBuilder.

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
    Copy the code

    The environment configuration is as follows

    <environments default="development">
      <environment id="development">
        <transactionManager type="JDBC">
          <property name="..." value="..."/>
        </transactionManager>
        <dataSource type="POOLED">
          <property name="driver" value="${driver}"/>
          <property name="url" value="${url}"/>
          <property name="username" value="${username}"/>
          <property name="password" value="${password}"/>
        </dataSource>
      </environment>
    </environments>
    Copy the code
  • MyBatis can execute different statements based on different database vendors. This multi-vendor support is based on the databaseId attribute in the mapping statement.

    <databaseIdProvider type="DB_VENDOR">
      <property name="SQL Server" value="sqlserver"/>
      <property name="DB2" value="db2"/>
      <property name="Oracle" value="oracle" />
    </databaseIdProvider>
    Copy the code
  • Mappers, which tells MyBatis where to find these SQL statements, are configured in four ways

    <! -- Use a resource reference relative to the classpath -->
    <mappers>
      <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
      <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
    
    <! -- Use fully qualified resource locator (URL) -->
    <mappers>
      <mapper url="file:///var/mappers/AuthorMapper.xml"/>
      <mapper url="file:///var/mappers/BlogMapper.xml"/>
      <mapper url="file:///var/mappers/PostMapper.xml"/>
    </mappers>
    
    <! Implement the fully qualified class name of the class using the mapper interface
    <mappers>
      <mapper class="org.mybatis.builder.AuthorMapper"/>
      <mapper class="org.mybatis.builder.BlogMapper"/>
      <mapper class="org.mybatis.builder.PostMapper"/>
    </mappers>
    
    <! Register all mapper interface implementations as mapper
    <mappers>
      <package name="org.mybatis.builder"/>
    </mappers>
    Copy the code

Each of the above attributes corresponds to a parsing method that uses XPath to parse the tag and returns a DefaultSqlSessionFactory object, which is the default implementation class of SqlSessionFactory. This is the initialization process of SqlSessionFactoryBuilder. The initialization process is the process of parsing the sub-tags of each/Configuration tag.

SqlSession

At the end of the MyBatis initialization process, which is called SqlSessionFactoryBuilder -> SqlSessionFactory, We can get the SqlSession from the SqlSessionFactory object and then execute the SQL statement. So let’s look at the process

In SqlSessionFactory. We can see that in the process of openSession will call in to the DefaultSqlSessionFactory openSessionFromDataSource method, This method primarily creates two objects that are important to our analysis of the execution flow, an Executor Executor object and a SqlSession object. Executors, which we’ll talk about next, let’s talk about SqlSession objects

SqlSession object is one of the most important objects in MyBatis. This interface allows you to execute commands, get mappings, and manage transactions. SqlSession allows you to perform simple CRUD operations. You can also use getMapper to access the Mapper layer and execute custom SQL statements. SqlSession allows you to start a session before executing SQL statements. Transaction operations are involved, so there are commit, rollback, close, and so on. This is also an application of the template design pattern.

MapperProxy

MapperProxy is the key object of Mapper mapping SQL statements. The Dao layer or Mapper layer we write is bound to the corresponding SQL statements through MapperProxy. Let’s explain the binding process

MyBatis (MyBatis, MyBatis, MyBatis, MyBatis, MyBatis, MyBatis, MyBatis, MyBatis, MyBatis, MyBatis) Third-rate enterprises do products).

SqlSession does not want to do things assigned to The Configuration hand, but Configuration also has a younger brother, it does not want to do things directly assigned to the younger brother, who is this younger brother? It’s called MapperRegistry, and we’re about to get to the heart of it. MapperRegistry is equivalent to the project manager, the project manager only from the general grasp of the project progress, do not need to know how his younger brother is working, to complete the task. Finally, the MapperProxyFactory does the real work. See this code Proxy newProxyInstance, do you have a feeling of suddenly enlighted, if you don’t have, suggested that check the dynamic Proxy, here recommend a (www.jianshu.com/p/95970b089…

In other words, the binding of Mapper and SQL statements in MyBatis is done by dynamic proxy.

Through dynamic proxy, we can easily define the interface in the Dao layer or Mapper layer, to achieve custom add, delete, change and check operations. So what is the specific implementation process? The above is just the binding process, don’t worry, let’s explore the SQL statement execution process.

Part of the code is blocked, a little bit more code, but it doesn’t affect our view of the main flow

The MapperProxyFactory generates a proxy object, which is MapperProxy, and eventually calls the mapperMethod.execute method, which is a long method with simple logic. Insert, update, delete, or query, and if it’s a query, it also determines the type of return value, so we can click on that and see how it’s designed.

A lot of the code can be ignored, but just look at the important points I’ve highlighted. We can see that no matter how many levels you go through, SqlSession is the standard.

Let’s take a look at the following implementation using selectList as an example.

This is the code for selectList in DefaultSqlSession, and we can see that executors appear, what is that? Let’s explain.

Executor

Remember when we talked about the concept of Executor earlier in the process? Let’s go back to where it first appeared.

The Configuration object creates an Executor object. What does this Executor do? Let’s get to know each other

Executor’s inheritance structure

Each SqlSession has an Executor object, which is responsible for adding, deleting, modifying, and querying. It can be interpreted as a encapsulated version of a JDBC Statement. An Executor is an Executor, and the Executor is an Executor.

Start with the Executor inheritance system

As shown in the figure above, at the top of the inheritance hierarchy is the Executor Executor, which has two implementation classes, BaseExecutor and CachingExecutor.

BaseExecutor is an abstract class. This way of implementing an interface through abstraction is the embodiment of the interface adaptation of the adapter design pattern. It is the default implementation of Executor and implements most of the functions defined by the Executor interface, reducing the difficulty of interface implementation. There are three subclasses of BaseExecutor: SimpleExecutor, ReuseExecutor, and BatchExecutor.

SimpleExecutor : The Statement object is the default executable used in MyBatis. The Statement object is opened every time an update or select is executed. Close the Statement object (either a Statement or a PreparedStatment object)

ReuseExecutor : The reusable executor, by reuse, means the reuse of the Statement. It uses an internal Map to cache all created statements. Each time a SQL command is executed, it checks whether a Statement object based on the SQL Statement exists. If a Statement object exists and the connection has not been closed, continue to use the previous Statement object and cache it. Since every SqlSession has a new Executor object, the Statement scope cached on ReuseExecutor is the same SqlSession.

BatchExecutor: BatchExecutor used to output multiple SQL to the database at once

CachingExecutor: a cache executor that queries results from the cache and returns the previous results if they exist. If not, delegate to an Executor delegate from the database. The delegate can be any of the above.

Executor creation and selection

We mentioned above that executors are created by Configuration, which is created based on the type of Executor, as shown below

This step is the creation of an executor, which is determined by the incoming ExecutorType. If you do not specify an ExecutorType, a simple executor is created by default. It can be assigned in two places:

  • Can be achieved by<settings>Tag to set all SqlSession objects in the current project to use the default Executor
<settings>
 <! -- SIMPLE, REUSE, BATCH -->
	<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
Copy the code
  • Another way to assign values to methods directly through Java
session = factory.openSession(ExecutorType.BATCH);
Copy the code

Executor execution process

The call chain for most of the Executor methods is pretty much the same. Here is an in-depth source code analysis of the execution process. If you don’t have the time or don’t feel like digging into it for a while, the following execution flowchart is a reference.

We continue our analysis following the selectList above, which calls the executor.query method.

The cacheingExecutor implementation class CachingExecutor is used to query whether the SQL statement has been executed for the first time. If the SQL statement has been executed for the first time, the cache will be created. If the same SQL statement is accessed a second time, it is fetched directly from the cache.

SelectList -> Query from the cache. May you see here some feel that the class is what things, I would like to encourage you to grasp the focus, do not have to see every piece of code, from find THE SQL call link, other code to see when you look, look at the source code is very easy to confuse, easy to be agitated, but remember one point, grasp the focus.

List = doQuery creates the Executor Executor to execute the SQL statement. If not, list = doQuery creates the Executor Executor to execute the SQL statement. Here we use a simple actuator.

At this point, the Executor completes its work, and the Executor turns over the rest of the work to the StatementHandler. Let’s get to know StatementHandler

StatementHandler

StatementHandler is the most important of the four components. It is responsible for manipulating Statement objects and interacting with the database. It also uses ParameterHandler and ResultSetHandler to map parameters. The result is bound to an entity class, two components we will discuss later.

When we set up native JDBC, we have this line of code

Statement stmt = conn.createStatement(); // You can also do this in PreparedStatement
Copy the code

This line of code creates a Statement object, or PreparedStatement object, which is managed by StatementHandler.

StatementHandler inheritance structure

Does it feel like the Executor inheritance system? The top interfaces are four component objects, with two implementation classes BaseStatementHandler and RoutingStatementHandler, BaseStatementHandler has three implementation classes, which are SimpleStatementHandler, PreparedStatementHandler, and CallableStatementHandler.

RoutingStatementHandler: The RoutingStatementHandler does not use the Statement object, but simply creates a proxy based on StatementType that represents the three implementation classes of the Handler. The StatementHandler interface object used when working with MyBatis is actually the RoutingStatementHandler object.

BaseStatementHandler: Another implementation class of the StatementHandler interface. BaseStatementHandler itself is an abstract class that simplifies the implementation of the StatementHandler interface. It is part of the adapter design pattern

  • SimpleStatementHandler: Manages Statement objects and pushes SQL statements to the database that do not require precompilation.
  • PreparedStatementHandler: management Statement object and to the data needed to push a precompiled SQL Statement.
  • CallableStatementHandler: Manages the Statement object and calls stored procedures in the database.

Notice here that the difference between SimpleStatementHandler and PreparedStatementHandler is whether the SQL statement contains variables and whether arguments are passed in externally.

SimpleStatementHandler is used to execute SQL with no arguments passed in

PreparedStatementHandler requires pre-parameter binding and assignment of variables and parameters passed in externally.

StatementHandler creation and source code analysis

The StatementHandler is created as follows

MyBatis will create StatementHandler according to the type of SQL statement. Let’s take preprocessing StatementHandler as an example

The executor not only creates the StatementHandler, but also creates the Statement object, sets the parameters, etc. After creating the PreparedStatement, we need to process the parameters.

If I were to draw a picture of the execution process, I think it would look like this

Let’s pause here to take a look at ParameterHandler, the third core component

ParameterHandler

ParameterHandler introduction

ParameterHandler is a ParameterHandler that dynamically assigns values to SQL statements in a PreparedStatement. The interface is simple, with only two methods

ParameterHandler has only one implementation class, DefaultParameterHandler, which implements both methods.

  • GetParameterObject: Reads parameters
  • SetParameters: Used to assign values to parameters in a PreparedStatement

Parsing of ParameterHandler

We discussed the creation of ParameterHandler above, so let’s continue with the parameterSize process above

This is the parsing of the specific parameters. Let’s describe it

public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  // parameterMappings encapsulates the parameters in #{} or ${}
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if(parameterMappings ! =null) {
    // If it is parameterized SQL, you need to loop through and set the values of the parameters
    for (int i = 0; i < parameterMappings.size(); i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      // If the parameter type is not OUT, this type is related to CallableStatementHandler
      // Because the stored procedure does not have output parameters, it needs to be set when the parameters are not output parameters.
      if(parameterMapping.getMode() ! = ParameterMode.OUT) { Object value;// Get the attribute name in #{}
        String propertyName = parameterMapping.getProperty();
        // If propertyName is the key in the Map
        if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
          // Get the value of additionalParameter by key
          value = boundSql.getAdditionalParameter(propertyName);
        }
        // If it is not the key in additionalParameters and the passed parameter is null, then value is null
        else if (parameterObject == null) {
          value = null;
        }
        // If the Class object for this parameter is already registered in typeHandlerRegistry, i.e. it is Primitive or String
        else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          // Otherwise, Map
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        // In a concrete implementation of parameterMappings derived from the PARSE method of SqlSource, we get typeHandler for parameterMappings
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        // Get the typeHandler JDBC type
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        } catch (SQLException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}
Copy the code

The following flowchart illustrates the parsing of ParameterHandler, using a simple executor as an example

After ParameterHandler preprocesses SQL parameters, we return to the doQuery method in SimpleExecutor

Another important component is ResultSetHandler. Let’s take a look at this component

ResultSetHandler

ResultSetHandler profile

Resultsetthandler is also a very simple interface

ResultSetHandler is an interface that has only one default implementation class, like ParameterHandler, whose default implementation class is DefaultResultSetHandler

ResultSetHandler Parsing process

MyBatis only has one default implementation class, DefaultResultSetHandler, which handles two things

  • Process the result set generated after the Statement is executed and generate a result list

  • Handles the output parameters of the stored procedure after execution

Encapsulate the corresponding object according to the ResultType or ResultMap configured in the Mapper file, and finally return the encapsulated object.

public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

  final List<Object> multipleResults = new ArrayList<Object>();

  int resultSetCount = 0;
  // Get the first result set
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  // Get the result map
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  // The size of the resulting map
  int resultMapCount = resultMaps.size();
  // The number of verification result mappings
  validateResultMapsCount(rsw, resultMapCount);
  // If the ResultSet wrapper is not null, and the number of ResultMaps is greater than the number of resultSets
  // resultSetCount must be 0 for the first time, so check whether ResultSetWrapper is 0
  while(rsw ! =null && resultMapCount > resultSetCount) {
    // Retrieves the number of resultSets from the resultMap
    ResultMap resultMap = resultMaps.get(resultSetCount);
    // Process result set, close result set
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  // Retrieve the result set from mappedStatement
  String[] resultSets = mappedStatement.getResultSets();
  if(resultSets ! =null) {
    while(rsw ! =null && resultSetCount < resultSets.length) {
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
      if(parentMapping ! =null) {
        String nestedResultMapId = parentMapping.getNestedResultMapId();
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
        handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; }}return collapseSingleResultList(multipleResults);
}
Copy the code

The main objects involved are:

ResultSetWrapper: wrapper for the result set. It is a layer wrapper for the result set. Its main properties are

  • ResultSet: The Java JDBC ResultSet interface represents the results of database queries. The text on queries shows how to return query results as java.sql.resultSet. This ResultSet is then iterated over to check the results.

  • TypeHandlerRegistry: A type registry that registers all Java types and type converters when initialized.

  • ColumnNames: Indicates the name of a field that needs to be returned in the query operation

  • ClassNames: Indicates the type of a field, that is, ColumnNames Indicates the type of each field name

  • JdbcTypes: JDBC Types, i.e. Java.sql. Types

  • ResultMap: Handles more complex mappings

The result map is processed in DefaultResultSetHandler and the above structure is returned to the calling client to complete the execution of a complete SQL statement.

Article Reference:

Advantages and disadvantages and characteristics of MyBatis

Mybatis base, MyBatis core profile properties element

Mybatis.org/mybatis-3/z…

Mybatis series (ten) -SQL execution process analysis (source code)

www.jianshu.com/p/19686af69…

www.mybatis.org/mybatis-3/g…

www.cnblogs.com/virgosnail/…

Blog.csdn.net/Roger_Coder…

Blog.csdn.net/qq924862077…

Mybatis source code analysis (eight)