The article directories

  • One, foreword
  • Two, MyBatis four-layer architecture
    • 2.1 Interface layer — two ways of interaction between Mybatis and database
      • 2.1.1 MyBatis uses Statement ID to interact with database, which does not comply with object-oriented and interface-oriented design principles
      • 2.1.2 Mybatis uses Mapper interface to interact with database, which conforms to object-oriented and interface oriented design principles
    • 2.2 Data processing layer
      • 2.2.1 Parameter Mapping
      • 2.2.2 Dynamic SQL Statement Generation
      • 2.2.3 Execution of SQL statements
      • 2.2.4 Result processing (emphasis: Encapsulating query result integration List)
    • 2.3 Framework support layer (cache + transaction + connection pool +SQL statement configuration (XML configuration + annotation configuration))
      • 2.3.1 Transaction management mechanism
      • 2.3.2 Connection Pool Management mechanism
      • 2.3.3 Cache mechanism
      • 2.3.4 CONFIGURATION methods of SQL Statements (XML Configuration + Annotation configuration (annotation @mapper and scanning annotation @mapperscan))
    • 2.4 Boot Layer (there are two ways to boot Mybatis: xmL-based configuration file and Java APi-based)
  • Three, the main components of MyBatis and their relationship
  • Four, practice: from MyBatis a SELECT query statement to analyze MyBatis architecture design
    • 4.1 From the user’s point of view
      • 4.1.1 Preparing database data, create the EMPLOYEES table, and insert data
      • 4.1.2 Configuring the Mybatis Configuration file named Mybatisconfig. XML (any name)
      • 4.1.3 POJO + XxxMapper. Java + XxxMapper. XML
      • 4.1.4 Dependencies: MyBatis and JDBC
      • 4.1.5 Client code
    • 4.2 View mybatis query above from source code point of view (important)
      • 4.2.1 Starting a Database Access Session – Create a SqlSession object
      • 4.2.2 Pass the Statement Id and parameters of a configured Sql Statement to SqlSession and return the result
      • 4.2.3 The MyBatis Executor executes the query() method based on the arguments passed in SqlSession
      • 4.2.4 The StatementHandler object sets query parameters in the Statement object, processes the resultSet returned by JDBC, and returns the resultSet as a List
        • prepareStatement()
      • 4.2.5 Further implementation of parameterize(Statement) for StatementHandler
      • 4.2.6 Implementation of StatementHandler’s List Query (Statement Statement, ResultHandler ResultHandler) method
      • 4.2.7 summary
  • 5. Interview Goldfinger
      • Mybatis overall architecture
  • Six, the summary

One, foreword

MyBatis is a very popular ORM framework, its function is very powerful, but its implementation is relatively simple, elegant. This paper mainly describes the MyBatis architecture design ideas, and discusses several core components of MyBatis, and then combined with a SELECT query example, in-depth code, to explore the implementation of MyBatis.

Mybatis is divided into interface layer, data processing layer, framework support layer and guidance layer from top to bottom. The second part is detailed

Two, MyBatis four-layer architecture

Mybatis interface layer summary: Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mybatis Statement ID = Mapper Finally, SqlSession is used to transfer SQL statements (mapper also uses method name and parameter list as method signature, determine statement ID, use SqlSesson call SQL statement). The interface layer plays a role in the whole Mybatis four layers, referring to the general diagram of the four layers: the interface layer is used to receive requests and call SQL statements.

The role of data processing layer in the four layers of Mybatis is referred to the general diagram of the four layers: Data processing layer, as the name implies, is used to process data. Data processing consists of four steps, including parameter mapping (query phase, data of Java type is converted into JDBC type data, and values are set by preparedStatement.setxxx (); In the return stage, jdbcType data in the resultset query resultset is converted into Java type data), SQL statement generation, SQL statement execution, and return value processing (for the returned resultset, one-to-many and many-to-one conversion of resultset relationship is supported, and there are two support modes. One is for nested query statements, and one is for nested result sets.

Below the data processing layer, the framework support layer provides technical support for data processing: four in total: cache, transaction, data source and connection pool, and support for two configuration modes (XML configuration and annotation + annotation driven).

The boot layer at the bottom is used to bootstrap the entire MyBatis startup and operation, including two ways: XML configuration mode and Java API mode.

2.1 Interface Layer – Two ways of interaction between Mybatis and the database

MyBatis interacts with the database in two ways:

A. Use the API provided by traditional MyBatis;

B. Use the Mapper interface

2.1.1 MyBatis uses Statement ID to interact with database, which does not comply with object-oriented and interface-oriented design principles

The Statement Id and query parameters are passed to the SqlSession object. The SqlSession object is used to interact with the database.

MyBatis provides a very convenient and simple API for users to realize the operation of adding, deleting, changing and checking data of the database, as well as the maintenance operation of database connection information and MyBatis own configuration information.

SQL > create a SqlSession object to interact with the database. SQL > create a SqlSession object to interact with the database. SQL > create a SqlSession object to interact with the database. SqlSession session = sqlSessionFactory.openSession(); / / SqlSession objects Blog Blog (Blog) session. = selectOne (” com. Foo. Beans. BlogMapper. SelectByPrimaryKey “, id); // statement Id

The above method of using MyBatis is to create a SqlSession object to deal with the database, and then operate the database according to the Statement Id and parameters. This method is simple and practical, but it does not conform to the concept of object-oriented language and the programming habit of interface programming. As Interface oriented programming is the general trend of object oriented, in order to adapt to this trend, MyBatis added a second way to support Interface (Interface) call.

2.1.2 Mybatis uses Mapper interface to interact with database, which conforms to object-oriented and interface oriented design principles

(1) MyBatis abstracts each node in the configuration file into a Mapper interface;

(2) Mapper interfaces declared in the method and node of < select | update | delete | insert > node corresponding item;

(3) < select | update | delete | insert > node id value for the method’s name in the Mapper interfaces, parameterType value indicates the Mapper corresponding method into the type, The resultMap value corresponds to the return value type represented by the Mapper interface or the element type of the return result set.

After configuration according to the configuration specification of MyBatis,

(1) instantiate Mapper interface implementation class: through sqlSession.getMapper (xxxmapper.class) method, MyBatis source code will be based on the corresponding interface declaration method information, through the dynamic proxy mechanism to generate a Mapper instance;

(2) The statement ID is determined by the method name and parameter list, and the underlying SQL statement is called by SqlSession. Sqlsession. select(” statementId “,parameterObject); select(” statementId “,parameterObject); Or SqlSession. Update (” statementId “, parameterObject); And so on to achieve the operation of the database;

MyBatis references Mapper interface for two reasons:

(1) In order to meet the needs of interface oriented programming.

(2) Enable users to configure SQL statements using annotations on the interface, which can be separated from the XML configuration file to achieve “0 configuration”.

Mybatis interface layer summary: The Mybatis interface layer provides two database access methods, Mybatis Statement ID and Mapper interface. The bottom layer is the same. The Statement ID is determined by the method name and parameter list as the method signature. Use SqlSesson to call SQL statements; The interface layer plays a role in the whole Mybatis four layers, as shown in the general diagram of the four layers: The interface layer is used to receive requests and start calling SQL statements.

2.2 Data processing layer

The data processing layer can be said to be the core of MyBatis. From a large perspective, it should complete two or four functions:

A. Build dynamic SQL statements by passing in parameters; (Parameter mapping + dynamic SQL statement generation)

B. Execute SQL statements and encapsulate query results to integrate List. (SQL execution + result processing)

2.2.1 Parameter Mapping

Parameter mapping refers to conversions between Java data types and JDBC data types:

There are two processes involved:

First, in the query phase, the Java type data is converted to JDBC type data and the value is set by preparedStatement.setxxx ().

Second, in the return phase, the jdbcType data in the resultset query resultset is converted to Java type data.

2.2.2 Dynamic SQL Statement Generation

Dynamic statement generation can be said to be a very elegant design of MyBatis framework. MyBatis uses Ognl to dynamically construct SQL statements by passing in parameter values, which makes MyBatis have strong flexibility and expansibility.

2.2.3 Execution of SQL statements

After the dynamic SQL statement is generated, MyBatis executes the SQL statement.

2.2.4 Result processing (emphasis: Encapsulating query result integration List)

Mybatis may convert the returned result set to a List after executing the SQL statement.

MyBatis supports one-to-many and many-to-one transformation of result set relation in the processing of result set, and has two support methods, one is the query of nested query statement, and the other is the query of nested result set.

Interviewer: What is your understanding of mybatis (Mapper interface nested statement query and nested result query)?

2.3 Framework support layer (cache + transaction + connection pool +SQL statement configuration (XML configuration + annotation configuration))

2.3.1 Transaction management mechanism

Transaction management mechanism is an indispensable part of ORM framework, and the quality of transaction management mechanism is also a criterion to evaluate whether an ORM framework is excellent.

2.3.2 Connection Pool Management mechanism

Since creating a database connection consumes a lot of resources, the design of connection pool is very important for applications with high data throughput and high traffic volume.

2.3.3 Cache mechanism

In order to improve the data utilization rate and reduce the pressure on the server and database, MyBatis will provide session-level data cache for some queries. It will place a query into SqlSession. Within the allowed time interval, MyBatis will directly return the cache results to users for identical queries. You don’t have to look it up in the database.

2.3.4 CONFIGURATION methods of SQL Statements (XML Configuration + Annotation configuration (annotation @mapper and scanning annotation @mapperscan))

MyBatis uses Statement ID to interact with database. MyBatis uses Mapper interface to interact with the database: XML configuration and annotation configuration can be used. Annotation configuration is recommended, which conforms to the idea of object-oriented. General Mapper is introduced later, and SQL statements in mapper. XML are no longer needed for single table query.

The traditional way of MyBatis to configure SQL statements is to use XML files for configuration, but this way does not well support the concept of interface oriented programming. In order to support interface oriented programming, MyBatis introduces the concept of Mapper interface. To make it possible to configure SQL statements with annotations, the user simply adds the necessary annotations to the interface (usually @mapper annotations) instead of configuring the XML file.

In Spring, @service @Controller annotation corresponds to @ComponentScan annotation, or in Mybatis, @mapper annotation corresponds to @mapperscan scan annotation, or XML configuration anyway, Annotation + scan is equivalent to XML configuration.

2.4 Boot Layer (there are two ways to boot Mybatis: xmL-based configuration file and Java APi-based)

The boot layer is the way to configure and launch MyBatis configuration information.

MyBatis provides two ways to boot MyBatis: XML configuration file based method and Java API based method.

Three, the main components of MyBatis and their relationship

From the perspective of MyBatis code implementation, the main core components of MyBatis are as follows:

Component name role
SqlSession (Interface Layer) As the main top-level API of MyBatis, it represents the session of interaction with the database, and completes the necessary function of adding, deleting, changing and searching the database
Executor (Data Processing Layer – Dynamic SQL statement generation) MyBatis executer, the core of MyBatis scheduling, is responsible for the generation of SQL statements and the maintenance of query cache
StatementHandler () Encapsulates JDBC Statement operations and performs operations on JDBC Statements, such as setting parameters and converting the Statement result set to a List.
ParameterHandler Responsible for converting user-passed parameters into parameters required for JDBC Statements
ResultSetHandler Converts the ResultSet object returned by JDBC into a collection of type List
TypeHandler Responsible for mapping and conversion between Java and JDBC data types
MappedStatement MappedStatement maintains a < SELECT
SqlSource Is responsible for dynamically generating SQL statements based on user-passed ParameterObjects, encapsulating the information into BoundSql objects, and returning them
BoundSql Represents dynamically generated SQL statements and parameter information
Configuration All Configuration information of MyBatis is maintained in the Configuration object.

(Note: this is just a list of the core components that I personally think belong to MyBatis, please do not prejudge, think that only these components! Everyone’s understanding of MyBatis is different, the results of analysis will naturally be different, welcome readers to question and different opinions, we discuss together ~)

Their relationship is shown below:

Mybatis and JDBC are two different things, MyBatis is a server program ORM framework, object relational mapping framework, the figure only explains myBatis ten components, not JDBC components. JDBC is a Java database connection tool, which is only used for Java statements, but can connect to different databases, usually mysql Mybatis under the server application, JDBC is above the database. JDBC is below.

Four, practice: from MyBatis a SELECT query statement to analyze MyBatis architecture design

4.1 From the user’s point of view

4.1.1 Preparing database data, create the EMPLOYEES table, and insert data

Create a basic employee information table"EMPLOYEES"(
        "EMPLOYEE_ID" NUMBER(6) not null,
       "FIRST_NAME" VARCHAR2(20),
       "LAST_NAME" VARCHAR2(25) not null,
       "EMAIL" VARCHAR2(25) not null unique,
       "SALARY"NUMBER 8, 2 (), the constraint"EMP_EMP_ID_PK" primary key ("EMPLOYEE_ID")); comment on table EMPLOYEES is'Staff Information Sheet';
    comment on column EMPLOYEES.EMPLOYEE_ID is 'employee id';
    comment on column EMPLOYEES.FIRST_NAME is 'first name';
    comment on column EMPLOYEES.LAST_NAME is 'last name';
    comment on column EMPLOYEES.EMAIL is 'email address';
    comment on column EMPLOYEES.SALARY is 'salary'; Insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY) values (100,'Steven'.'King'.'SKING', 24000.00);
	
	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
	values (101, 'Neena'.'Kochhar'.'NKOCHHAR', 17000.00);
	
	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
	values (102, 'Lex'.'De Haan'.'LDEHAAN', 17000.00);
	
	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
	values (103, 'Alexander'.'Hunold'.'AHUNOLD', 9000.00);
	
	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
	values (104, 'Bruce'.'Ernst'.'BERNST', 6000.00);
	
	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
	values (105, 'David'.'Austin'.'DAUSTIN', 4800.00);
	
	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
	values (106, 'Valli'.'Pataballa'.'VPATABAL', 4800.00);
	
	insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)
	values (107, 'Diana'.'Lorentz'.'DLORENTZ', 4200.00);    
Copy the code

4.1.2 Configuring the Mybatis Configuration file named Mybatisconfig. XML (any name)

<? xml version="1.0" encoding="utf-8"? > <! DOCTYPE configuration PUBLIC"- / / mybatis.org//DTD Config / 3.0 / EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC" />
      <dataSource type="POOLED">
	 <property name="driver" value="oracle.jdbc.driver.OracleDriver" />  
         <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />  
         <property name="username" value="louis" />  
         <property name="password" value="123456" />
      </dataSource>
    </environment>
  </environments>
    <mappers>
       <mapper  resource="com/louis/mybatis/domain/EmployeesMapper.xml"/>
    </mappers>
</configuration>
Copy the code

Mapper can be identified in two ways: mybatisconfig. XML configuration or @mapper annotation + @mapperscan annotation

4.1.3 POJO + XxxMapper. Java + XxxMapper. XML

package com.louis.mybatis.model;
 
import java.math.BigDecimal;
 
public class Employee {
    private Integer employeeId;
 
    private String firstName;
 
    private String lastName;
 
    private String email;
 
    private BigDecimal salary;
 
    public Integer getEmployeeId() {
        return employeeId;
    }
 
    public void setEmployeeId(Integer employeeId) {
        this.employeeId = employeeId;
    }
 
    public String getFirstName() {
        return firstName;
    }
 
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
 
    public String getLastName() {
        return lastName;
    }
 
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public BigDecimal getSalary() {
        returnsalary; } public void setSalary(BigDecimal salary) { this.salary = salary; }}Copy the code
<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE mapper PUBLIC"- / / mybatis.org//DTD Mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.louis.mybatis.dao.EmployeesMapper" >
 
  <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Employee" >
    <id column="EMPLOYEE_ID" property="employeeId" jdbcType="DECIMAL" />
    <result column="FIRST_NAME" property="firstName" jdbcType="VARCHAR" />
    <result column="LAST_NAME" property="lastName" jdbcType="VARCHAR" />
    <result column="EMAIL" property="email" jdbcType="VARCHAR" />
    <result column="SALARY" property="salary" jdbcType="DECIMAL" />
  </resultMap>
  
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    	EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY
    	from LOUIS.EMPLOYEES
    	where EMPLOYEE_ID = #{employeeId,jdbcType=DECIMAL}
  </select>
</mapper>
Copy the code

Xxxmapper.xml is used to write SQL statements. After using universal mapper, single table query does not need to write SQL statements in xxxmapper.xml.

4.1.4 Dependencies: MyBatis and JDBC

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> < modelVersion > 4.0.0 < / modelVersion > < groupId > batis < / groupId > < artifactId > batis < / artifactId > < version > 0.0.1 - the SNAPSHOT < / version > < packaging > jar < / packaging > < name > batis < / name > < url > http://maven.apache.org < / url > <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.7</version> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc14</artifactId> < version > 10.2.0.4.0 < / version > < / dependency > < / dependencies > < project >Copy the code

4.1.5 Client code

package com.louis.mybatis.test; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.louis.mybatis.model.Employee; /** * SqlSession Simple query demo * @author louluan */ public class SelectDemo {public static void main(String[] args) throws Exception { /* * 1. SqlSessionFactory = SqlSessionFactory = SqlSessionFactory = SqlSessionFactory = SqlSessionFactory = SqlSessionFactory SqlSessionFactory only need to create a, as a singleton to use * / InputStream InputStream = Resources. The getResourceAsStream ("mybatisConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(inputStream); SqlSession SqlSession = factory.openSession(); Params = new HashMap<String,Object>(); params.put("min_salary", 10000); List<Employee> result = sqlsession.selectList ("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params); List<Employee> result1 = sqlsession.selectList ("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary");
		System.out.println("Number of employees making less than 10,000:"+result.size()); //~output: total output: 5 system.out.println ("Number of employees:"+result1.size()); //~output: number of employees: 8}}Copy the code

From 4.1.1 to 4.1.5, table design, mybatisconfig.xml (mapper two ways, XML configuration or @mapper + @mapperscan), POJO+ xxxmapper. Java + xxxmapper. XML (use general Mapper, single table operation without writing SQL statements), dependency import (Mybatis dependency and JDBC dependency), client code (from the configuration File InputStream, then SqlSessionFactoryBuilder, then SqlSessionFactory, then SqlSession, pass in statementid and parameters, execute SQL statement, get result set)

4.2 View mybatis query above from source code point of view (important)

This section describes the entire working process of SqlSession

4.2.1 Starting a Database Access Session – Create a SqlSession object

SqlSession sqlSession = factory.openSession();
Copy the code

MyBatis encapsulates the access to the database, the database session and transaction control into the SqlSession object.

Goldfinger: Two contents in SqlSession: transaction control + data query

4.2.2 Pass the Statement Id and parameters of a configured Sql Statement to SqlSession and return the result

List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);
Copy the code

The above “com. Louis. Mybatis. Dao. EmployeesMapper. SelectByMinSalary”, is configured in EmployeesMapper. XML Statement ID, params query parameter is passed.

Let’s look at the definition of the sqlsession.selectList () method:

  public <E> List<E> selectList(String statement, Object parameter) {
    returnthis.selectList(statement, parameter, RowBounds.DEFAULT); } public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //1. According to Statement Id, To look for in the mybatis Configuration object Configuration and Configuration files corresponding MappedStatement MappedStatement ms = Configuration. GetMappedStatement (statement); MyBatis Executor List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);return result;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database. Cause: "+ e, e); } finally { ErrorContext.instance().reset(); }}Copy the code

MyBatis at the time of initialization, all MyBatis Configuration information will be loaded into memory, using org. Apache. The ibatis. Session. The Configuration examples to maintain. The user can use the sqlSession. GetConfiguration () method to get. The organization format of the configuration information in the configuration file of MyBatis is almost exactly the same as the organization format of the objects in memory. In the example above

  <select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" >
    select 
    	EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY
    	from LOUIS.EMPLOYEES
    	<if test="min_salary ! = null">
    		where SALARY < #{min_salary,jdbcType=DECIMAL}
    	</if>
  </select>
Copy the code

Loaded into the memory will generate a corresponding MappedStatement object, then to key = “com. Louis. Mybatis. Dao. EmployeesMapper. SelectByMinSalary”, Value is in the form of an MappedStatement object and is maintained in a Map in the Configuration file. When you need to use it later, you just need to get the Id value.

From the above code we can see that the SqlSession does the following:

SqlSession obtains the corresponding MappedStatement object from the Mybatis Configuration object based on the Statement ID, and then calls the Mybatis actuator to perform the specific operation. //1. Locate the MappedStatement corresponding to the Configuration file in the Mybatis Configuration object based on the Statement Id. Configuration is the key, the value here, according to the key value MappedStatement ms = Configuration. Get getMappedStatement (statement); List result = Executor. Query (ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

SqlSession mission complete, see Executor performance.

4.2.3 The MyBatis Executor executes the query() method based on the arguments passed in SqlSession

Public <E> List<E> query(MappedStatement MS, Object parameter, RowBounds RowBounds, ResultHandler resultHandler) throws SQLException { // 1. BoundSql = Ms. GetBoundSql (parameter); BoundSql = Ms. GetBoundSql (parameter); CacheKey Key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
 
  @SuppressWarnings("unchecked")
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if(list ! = null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); }elseList = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; }if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      deferredLoads.clear(); // issue # 601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue # 482}}returnlist; } private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); List = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); // query (ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
Copy the code

Further down the line, how does the doQuery() method perform?

/** ** SimpleExecutor class doQuery() ** / public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //5. According to the existing parameters, Create StatementHandler object to perform query operations StatementHandler handler = configuration. NewStatementHandler (wrapper, ms, parameter, rowBounds, resultHandler, boundSql); STMT = prepareStatement(handler, Ms. GetStatementLog ()); STMT = prepareStatement(handler, Ms. GetStatementLog ()); STMT = prepareStatement(handler, Ms. GetStatementLog ()); //7. Call the statementhandler.query () method to return the List result setreturnhandler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); }}Copy the code

The executor.query () method creates a StatementHandler object, passes the necessary parameters to StatementHandler, and uses StatementHandler to query the database. Finally, the List result set is returned.

As you can see from the code above, Executor does:

BoundSql object is generated for StatementHandler to use.

BoundSql boundSql = ms.getBoundSql(parameter); (2) Create a cache for queries to improve performance (the specific caching mechanism is not the focus of this article, I will discuss it separately, interested readers can follow my other blog posts); CacheKey Key = createCacheKey(ms, parameter, rowBounds, boundSql); // This query() uses both dynamically generated boundSql and the cache keyreturnquery(ms, parameter, rowBounds, resultHandler, key, boundSql); Bash //4. List = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list);if
(ms.getStatementType() == StatementType.CALLABLE) {  
localOutputParameterCache.putObject(key, parameter); } return list;
Copy the code

In doQuery(), create a JDBC Statement connection object, pass it to the StatementHandler object, and return the List query result.

//5. According to the existing parameters, Create StatementHandler object to perform query operations StatementHandler handler = configuration. NewStatementHandler (wrapper, ms, parameter, rowBounds, resultHandler, boundSql); STMT = prepareStatement(handler, Ms. GetStatementLog ()); STMT = prepareStatement(handler, Ms. GetStatementLog ()); STMT = prepareStatement(handler, Ms. GetStatementLog ()); //7. Call the statementhandler.query () method to return the List result setreturnhandler.<E>query(stmt, resultHandler); ` ` `Copy the code

All right, Executor’s job is done. Watch StatementHandler and Statement in action

4.2.4 The StatementHandler object sets query parameters in the Statement object, processes the resultSet returned by JDBC, and returns the resultSet as a List

prepareStatement()

Following Executor step 6 above, look at the implementation of the prepareStatement() method:

/** ** SimpleExecutor class doQuery() ** / public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); STMT = prepareStatement(handler, Ms. GetStatementLog ()); // 1. Prepare the Statement object and set the parameter STMT = prepareStatement(handler, Ms. GetStatementLog ()); // 2. StatementHandler executes the query() method and returns the List resultreturnhandler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt;  Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); // Set parameters for the Statement object. Set to the specified parameter handler.parameterize(STMT);return stmt;
  }
Copy the code

The StatementHandler object does two things:

(1. Set parameters: In JDBC PreparedStatement type object creation process, we use SQL statement string contains how many? Placeholders, which we will set later.

StatementHandler sets the value of statement using the parameterize(statement) method.

(2) query: StatementHandler executes the Statement using the List Query (Statement Statement, ResultHandler ResultHandler) method. Encapsulate the resultSet returned by the Statement object as a List.

StatementHandler does two things, the first of which is not done yet, but I’ll go further

4.2.5 Further implementation of parameterize(Statement) for StatementHandler

/** * Implements the parameterize(statement) method of the StatementHandler class. */ public void parameterize(statement statement) throws parameterize SQLException {/ / using ParameterHandler object to complete the set value of the Statement. ParameterHandler setParameters ((PreparedStatement) Statement); }Copy the code
/** ** ParameterHandler implements setParameters(PreparedStatement ps) * to setParameters for a Statement */ public void setParameters(PreparedStatement ps) throws SQLException { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if(parameterMappings ! = null) {for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if(parameterMapping.getMode() ! = ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else{ MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } // Each Mapping has a TypeHandler, To set up a preparedStatement parameters according to the TypeHandler TypeHandler TypeHandler = parameterMapping. GetTypeHandler (); JdbcType jdbcType = parameterMapping.getJdbcType();if(value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull(); SetParameter (ps, I + 1, value, jdbcType); // Set the Java type to JDBC typeHandler.setParameter(ps, I + 1, value, jdbcType); }}}}Copy the code

Parameterize (Statement) calls ParameterHandler’s setParameters(Statement) method.

The ParameterHandler setParameters(Statement) method is responsible for calling the? Statement object based on the parameters we entered. Assignment at the placeholder.

bash typeHandler.setParameter(ps, i + 1, value, jdbcType);

Get a parameterMappings based on boundSql dynamic Sql, then iterate through the list. For each parameterMapping, get the propertyName from parameterMapping, Get the value from propertyName, then get the typeHandler from parameterMapping, then get the jdbcType from parameterMapping, SetParameter (ps, I + 1, value, jdbcType);

The StatementHandler does two things. The first one sets the parameters and the second query starts

4.2.6 Implementation of StatementHandler’s List Query (Statement Statement, ResultHandler ResultHandler) method

*/ public <E> List<E> query(Statement Statement, ResultHandler resultHandler) throws SQLException { // 1. PreparedStatement ps = (PreparedStatement) Statement; preparedStatemnt.execute(); ps.execute(); //2. Use a ResultHandler to process a ResultSetreturn resultSetHandler.<E> handleResultSets(ps);
  }
Copy the code

StatementHandler hands the query to ResultSetHandler. The following protagonist becomes ResultSetHandler

/** * The handleResultSets() method of DefaultResultSetHandler implements ** / public List<Object> handleResultSets(Statement STMT) throws SQLException { final List<Object> multipleResults = new ArrayList<Object>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount);while(rsw ! = null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); ResultSet handleResultSet(RSW, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResulSets();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



StatementHandler’s List Query (Statement Statement, ResultHandler ResultHandler) The ResultSetHandler handleResultSets(Statement) method is called. ResultSetHandler’s handleResultSets(Statement) method converts the resultSet generated after the Statement Statement to a List resultSet.

4.2.7 summary

The first protagonist, SqlSession

SqlSession obtains the corresponding MappedStatement object from the Mybatis Configuration object based on the Statement ID, and then calls the Mybatis actuator to perform the specific operation. //1. Locate the MappedStatement corresponding to the Configuration file in the Mybatis Configuration object based on the Statement Id. Configuration is the key, the value here, according to the key value MappedStatement ms = Configuration. Get getMappedStatement (statement); List result = Executor. Query (ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

SqlSession mission complete, see Executor performance.

The second protagonist, Executor

Executor’s functions and functions are:

BoundSql object is generated for StatementHandler to use.

BoundSql boundSql = ms.getBoundSql(parameter); (2) Create a cache for queries to improve performance (the specific caching mechanism is not the focus of this article, I will discuss it separately, interested readers can follow my other blog posts); CacheKey Key = createCacheKey(ms, parameter, rowBounds, boundSql); // This query() uses both dynamically generated boundSql and the cache keyreturnquery(ms, parameter, rowBounds, resultHandler, key, boundSql); Bash //4. List = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list);if
(ms.getStatementType() == StatementType.CALLABLE) {  
localOutputParameterCache.putObject(key, parameter); } return list;
Copy the code

In doQuery(), create a JDBC Statement connection object, pass it to the StatementHandler object, and return the List query result.

//5. According to the existing parameters, Create StatementHandler object to perform query operations StatementHandler handler = configuration. NewStatementHandler (wrapper, ms, parameter, rowBounds, resultHandler, boundSql); STMT = prepareStatement(handler, Ms. GetStatementLog ()); STMT = prepareStatement(handler, Ms. GetStatementLog ()); STMT = prepareStatement(handler, Ms. GetStatementLog ()); //7. Call the statementhandler.query () method to return the List result setreturnhandler.<E>query(stmt, resultHandler); ` ` `Copy the code

All right, Executor’s job is done. Watch StatementHandler and Statement in action

The third protagonist, StatementHandler

The StatementHandler object does two things:

(1. Set parameters: In JDBC PreparedStatement type object creation process, we use SQL statement string contains how many? Placeholders, which we will set later.

StatementHandler sets the value of statement using the parameterize(statement) method.

The fourth protagonist, ParameterHandler, and the fifth protagonist, TypeHandler

bash typeHandler.setParameter(ps, i + 1, value, jdbcType);

Get a parameterMappings based on boundSql dynamic Sql, then iterate through the list. For each parameterMapping, get the propertyName from parameterMapping, Get the value from propertyName, then get the typeHandler from parameterMapping, then get the jdbcType from parameterMapping, SetParameter (ps, I + 1, value, jdbcType);

(2) query: StatementHandler executes the Statement using the List Query (Statement Statement, ResultHandler ResultHandler) method. Encapsulate the resultSet returned by the Statement object as a List.

The sixth protagonist, ResultSetHandler

StatementHandler hands the query to ResultSetHandler. The following protagonist becomes ResultSetHandler

The return should also use the TypeHandler class to convert the JDBC type to a Java type, but it is not found. Search for getTypeHandler() in the DefaultResultSetHandler class.

5. Interview Goldfinger

Mybatis overall architecture

Six, the summary

MyBatis architecture design, completed.

Play code every day, progress every day!!