StatementHandler of MyBatis core configuration overview

StatementHandler of MyBatis core configuration overview

StatementHandler of the four major components of MyBatis

StatementHandlerIt is the most important object among the four components. It is responsible for operating the Statement object to communicate with the database. It also uses ParameterHandler and ResultSetHandler to map parameters and bind the results to entity classes during work.

When we build native JDBC, there will be such a line of code

Statement stmt = conn.createStatement(); //也可以使用PreparedStatement来做

The Statement object or PreparedStatement object created by this line of code is managed by StatementHandler.

Basic composition of StatementHandler

Let's take a look at the main methods in StatementHandler:

  • prepare : used to create a concrete Statement object implementation class or Statement object
  • parametersize : Used to initialize Statement objects and assign values ​​to sql placeholders
  • update : Used to notify the Statement object to push insert, update, delete operations to the database
  • query : Used to notify the Statement object to push the select operation to the database and return the corresponding query result

Inheritance structure of StatementHandler

Does it feel Executorvery similar to the inheritance system of ? The top-level interface is the four major component objects, which have two implementation classes BaseStatementHandlerand RoutingStatementHandler BaseStatementHandler has three implementation classes, which are SimpleStatementHandler, PreparedStatementHandler and CallableStatementHandler.

RoutingStatementHandler : RoutingStatementHandler does not use the Statement object, but only creates a proxy based on the StatementType, and the proxy is the three implementation classes corresponding to the Handler. **When working in MyBatis, the StatementHandler interface object used is actually the RoutingStatementHandler object.**We can understand it as

StatementHandler statmentHandler = new RountingStatementHandler();
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
    

  // 根据 statementType 创建对应的 Statement 对象
  switch (ms.getStatementType()) {
    
    
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case CALLABLE:
      delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    default:
      throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
  }

}

BaseStatementHandler : Another implementation class of the StatementHandler interface. It is an abstract class itself. It is used to simplify the difficulty of implementing the StatementHandler interface. It belongs to the embodiment of the adapter design pattern . It mainly has three implementation classes

  • SimpleStatementHandler : Manage Statement objects and push SQL statements that do not require precompilation to the database
  • PreparedStatementHandler : Manage Statement objects and push SQL statements that need to be precompiled to the data,
  • CallableStatementHandler : Manage Statement objects and call stored procedures in the database

StatementHandler object creation and source code analysis

The StatementHandler object is called by the newStatementHandler in the Configuration object when the SqlSession object receives a command operation, that is to say, the newStatementHandler in the Configuration is provided by the query and update (insert, update, delete) methods in the executor. StatementHandler is actually managed and created by Executor.

SimpleExecutor.java

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,解析SQL语句
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 由handler来对SQL语句执行解析工作
      return handler.<E>query(stmt, resultHandler);
    } finally {
    
    
      closeStatement(stmt);
    }
  }

As can be seen from the figure, StatementHandler creates a RoutingStatementHandler by default, which is the default implementation of StatementHandler. RoutingStatementHandler is responsible for creating a corresponding StatementHandler according to StatementType to handle calls.

Prepare method call process analysis

The calling process of the prepare method is as follows. In the above source code analysis process, we have analyzed that the Executor will create a StatementHandler object when executing SQL statements, and then go through a series of StatementHandler type judgments and initialization. When you get the statementhandler object returned by StatementHandler, its prepareStatement()method will be called. Let’s take a look at preparedStatement()the method together (we take a simple executor as an example, because the process of creating its StatementHandler object is similar to the process of executing the preparedStatement() method ):

SimpleExecutor.java

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,解析SQL语句
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    
    stmt = prepareStatement(handler, ms.getStatementLog());
    
    // 由handler来对SQL语句执行解析工作
    return handler.<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, transaction.getTimeout());
  
  handler.parameterize(stmt);
  return stmt;
}

// prepare方法调用到 StatementHandler 的实现类RoutingStatementHandler,再由RoutingStatementHandler调用BaseStatementHandler中的prepare 方法

// RoutingStatementHandler.java
@Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    
    
    return delegate.prepare(connection, transactionTimeout);
  }

//  BaseStatementHandler.java
 @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    
    
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
    
    
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } ...

The most important method is instantiateStatement()the method. When the object of the database connection is obtained, the instantiateStatement()method will be called. The instantiateStatement method is located in the StatementHandler. It is an abstract method implemented by subclasses. The actual implementation is one of the three StatementHandlers. species, we also take as SimpleStatementHandleran example

protected Statement instantiateStatement(Connection connection) throws SQLException {
    
    
    if (mappedStatement.getResultSetType() != null) {
    
    
      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
    
    
      return connection.createStatement();
    }
  }

From the above code, we can see that instantiateStatement() finally returns a Statement object. After a series of calls, the statement object will be returned to the SimpleExecutor simple executor for use by the parametersize method . That is to say, the prepare method is responsible for generating the Statement instance object, and the parameterize method is used to process the corresponding parameters of the Statement instance.

Parametersize method call process analysis

The parametersize method is easier to see, and the parametersize method call is also managed through the executor. This time we still want to use SimpleStatementHandler as an example but it doesn’t work? why? Because SimpleStatementHandler is an empty implementation, why is it null? Because SimpleStatementHandler is only responsible for processing simple SQL, it can directly query the resulting SQL, for example:

select studenname from Student

And SimpleStatementHandler does not involve the assignment of parameters, so where should the assignment of parameters be performed? In fact, the step of assigning values ​​to parameters is PreparedStatementHandlerperformed in , so our main focus is on the parameterize method in PreparedStatementHandler

public void parameterize(Statement statement) throws SQLException {
    
    
  parameterHandler.setParameters((PreparedStatement) statement);
}

We can see that the work of assigning values ​​to parameters is done by an object called parameterHandler. Are they all like this? Take a look at CallableStatementHandler

public void parameterize(Statement statement) throws SQLException {
    
    
  registerOutputParameters((CallableStatement) statement);
  parameterHandler.setParameters((CallableStatement) statement);
}

As you can see from the above code, CallableStatementHandler is also assigned parameters by parameterHandler.

So what exactly is this parameterHandler? This question can be thought of to show that you are already on the road, and this is the third component of our actuator. We will analyze this component in the next section

Update method call process analysis

Use a flow chart to represent the calling process:

Briefly describe the execution process of the update method:

  1. After MyBatis receives the update request, it will first find the CachingExecutor cache executor to query whether the cache needs to be refreshed, and then find the BaseExecutor to execute the update method;
  2. The BaseExecutor basic executor will clear the first-level cache, and then hand it over to find the corresponding executor according to the type of executor, and continue to execute the update method;
  3. The specific executor will first create the Configuration object, call the newStatementHandler method according to the Configuration object, and return the statementHandler handle;
  4. The specific executor will call the prepareStatement method. After finding the prepareStatement method of this class, the prepareStatement method will call the prepare method in the BaseStatementHandler subclass of StatementHandler
  5. The prepare method in BaseStatementHandler will call instantiateStatement to instantiate a specific Statement object and return it to the specific executor object
  6. The parameterize method is called by the specific executor object to assign values ​​to the parameters.

Continuing from the above parametermethod, the details will be handed over to ParameterHandlerfor further assignment processing

The Query query method is almost the same as the update method, so I won't give a detailed example here

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132642163