This article is a summary note of the book “MyBatis: Technical Principles and Practices”.

The last article covered reflection and dynamic proxy basics, mainly to set the stage for this article. Reflection makes configuration and flexibility much better. You can set parameters for many configurations, and dynamic proxies can create proxy objects at runtime and do some special processing.

Article Index:

  1. Introduction to JDBC and MyBatis
  2. All configurations for MyBatis
  3. “Mapper” fully understood
  4. Reflection and dynamic proxy basis
  5. MyBatis plug-in and development process

This article will introduce the principle of MyBatis parsing and operation. The next article will introduce plug-ins and applications. The purpose of this article is to better write plug-ins.

  • Build the SqlSessionFactory procedure
  • Dynamic proxy for the mapper
  • SqlSession 4 large objects
  • The process of SQL execution

SqlSessionFactory and SqlSession are the core components of MyBatis, which are described in detail in the JDBC and MyBatis introduction.

Build the SqlSessionFactory procedure

The construction is divided into two steps:

  • Parse configuration XML file through XMLConfigBuilder, read configuration parameters, including basic configuration XML file and mapper XML file;
  • SqlSessionFactory is an interface that provides a default implementation class DefaultSqlSessionFactory.

In plain English, we parse all our configurations into a Configuration object, which we can use to obtain the required configurations throughout the lifetime.

Since the plug-in needs frequent access to the internal components of the mapper, we will focus on this part to understand the objects abstracted from this configuration:

MappedStatement

It holds the mapping of a node (select | insert | delete | update), including the configuration of SQL, SQL id, cache information, resultMap, parameterType, resultType important configuration content, etc.

It involves more objects, generally do not modify it.

SqlSource

It is a property of MappedStatement, which is used to assemble SQL according to parameters and other rules. It is also complex, and generally does not need to be modified.

BoundSql

For parameters and SQL, it is mainly reflected in BoundSql class object. In the plug-in, the current running SQL, parameters and parameter rules can be obtained through it, and appropriate modifications can be made to meet special requirements.

BoundSql provides three main attributes: parameterObject, parameterMappings, and SQL, which are described below.

ParameterObject is the parameter itself. You can pass parameters of simple objects, POJOs, maps, or @param annotations:

  • Passing a simple object (int, float, String, etc.) converts the argument to the corresponding class. For example, int is converted to an Integer.
  • If a POJO or Map is passed, paramterObject is the same as the POJO or Map passed in;
  • If you pass multiple parameters and no @param annotation, parameterObject is a Map

    Object with the form {“1”:p1, “2”:p2, “3”:p3… “param1”:p1 , “param2”:p2 , “param3”,p3 … }, so you can use #{param1} or #{1} to refer to the first argument;
    ,object>
  • If multiple arguments are passed, the @param annotation is used, which is similar to the unannotated annotation, but the key of the sequence number is replaced by the name specified by @param.

ParameterMappings, which is a List in which the element is a ParameterMapping object, will represent parameter references in SQL, including name, expression, javaType, jdbcType, typeHandler, and so on.

SQL is a piece of SQL written in the mapper.

With the Configuration object, building the SqlSessionFactory is simple:

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

SqlSession running process

Dynamic proxy for the mapper

Mapper mapping is implemented through a dynamic proxy, using the JDK dynamic proxy to return a proxy object that can be accessed by the caller.

First look at the class that implements the InvocationHandler interface, which is the key to executing this proxy method. You can see that Mapper is an interface that generates a MapperMethod object and calls the Execute method.

public class MapperProxy<T> implements InvocationHandler.Serializable {...@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        returninvokeDefaultMethod(proxy, method, args); }}catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    returnmapperMethod.execute(sqlSession, args); }}Copy the code

Look at the following code, MapperMethod takes command mode, according to the SQL operation, do different processing.

public class MapperMethod {
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break; . }}}Copy the code

Finally, the method to generate the Proxy class is created using the JDK dynamic Proxy.

public class MapperProxyFactory<T> {

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), newClass[] { mapperInterface }, mapperProxy); }}Copy the code

The Mapper object is a proxy object. When one of its methods is called, it actually calls the MapperProxy#invoke method. The Mapper XML file namespace corresponds to the full path of the interface. Locate the SQL and finally use the methods of the SqlSession interface to enable it to execute the query.

Four objects under SqlSession

According to the above analysis, the mapper is a dynamic proxy object, which enters the Execute method of MapperMethod. After a simple judgment, it enters the delete, update, insert, select and other methods of SqlSession. How to execute these methods is described below.

The processes executed by Mapper are Executor, StatementHandler, ParameterHandler, and ResultHandler to perform database operations and return results. Understanding these processes is key to writing plug-ins:

  • Executor: An Executor that uniformly schedules three other objects to execute the corresponding SQL;
  • StatementHandler: Uses the database Statement to perform operations.
  • ParameterHandler: used for SQL parameter processing.
  • ResultHandler: Performs the encapsulation and return processing of the final data set;

There are three kinds of actuators in MyBatis:

  • SIMPLE: SIMPLE actuator, the default actuator;
  • REUSE: Executes a pre-processed statement for REUSE.
  • BATCH: executes reuse statements and BATCH updates, for the BATCH specific executor;

Take SimpleExecutor as an example to illustrate the execution process

public class SimpleExecutor extends BaseExecutor {

  /** * Execute the query operation */
  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(this, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally{ closeStatement(stmt); }}/** * Initialize StatementHandler */
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }
  
  /** * Execute query */
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    returnresultSetHandler.<E>handleResultSets(statement); }}Copy the code

As you can see, it ends up being delegated to the StatementHandler sessifier, which is an interface that actually creates a RoutingStatementHandler object, but it’s not a real service object, it’s executed using the adapter pattern to find the corresponding StatementHandler. In MyBatis, StatementHandler and Executor are divided into three types: SimpleStatementHandler, PreparedStatementHandler, and CallableStatementHandler.

Executor precompiles the SQL statement by calling the Prepare method of StatementHandler, and sets some basic running parameters. Then call parameterize() method to enable ParameterHandler to set parameters, complete the pre-compilation, execute the query, and return the result to the caller with the encapsulated ResultHandler.

The parameter handler and the result handler are relatively simple and will not be covered here.

The next article will look at plug-ins and their applications, which extend the four major objects during SQL execution.

MyBatis plug-in and development process

Please scan the qr code below and follow my wechat official account ~