In the previous article we looked at the three types of Executor executors, and each of the Update and Query methods has a Statement and a StatementHandler operation. In this article we will cover the relationship between Statement and StatementHandler, as well as their implementation details.

series

MyBatis principle series (a)- hand handle with you to read MyBatis source code MyBatis principle series (two)- hand handle with you to understand MyBatis startup process MyBatis principle series (3) – take you understand the SqlSession, holding SqlSessionFactory, principle of the relationship between the SqlSessionFactoryBuilder is MyBatis series (4) – handy belt you know MyBatis Executor of the actuator MyBatis principles series (6)- Learn how to create a BoundSql Statement, StatementHandler, and mappedsql MyBatis principle series (9)- MyBatis principle series (9)- MyBatis with your understanding of the transaction management mechanism

1. The Statement object

In the original JDBC operation, you load the driver, set properties, obtain connections, and create a Statement object… And so on. In JDBC operations, the Statement object sends an SQL Statement to the database and retrieves the execution result. There are three types of Statement objects: Statement, PreparedStatement, and CallableStatement. Their inheritance is as follows

  1. Statement: Can send SQL as a string, but does not support passing parameters. This applies to static SQL statements.
  2. PreparedStatement: a precompiled SQL statement that accepts arguments and prevents SQL injection to improve security. Sql statements are compiled in the database system and can be used to execute the same Sql multiple times. Therefore, they perform better than statements.
  3. CallableStatement: used when executing stored procedures, and can accept arguments passed in.

The Statement interface has many methods, which are used to perform updates, queries, and fetch results.

2. StatementHandler object

2.1 StatementHandler Object Acquaintance

StatementHandler objects are literally managing Statement objects. It has two direct implementations, BaseStatementHandler and RoutingStatementHandler. And then BaseStatementHandler has three implementations: SimpleStatementHandler, PreparedStatementHandler, CallableStatementHandler, They manage the Statement, PreparedStatement, and CallableStatement objects described above. BaseStatementHandler uses the adapter pattern to reduce the complexity of implementing interfaces, while RoutingStatementHandler wraps all three types of handlers. Operates as a proxy class.

The methods of the StatementHandler interface are as follows

/ * * *@author Clinton Begin
 */
public interface StatementHandler {

  // Create a Statement object
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  // Assign placeholders in Sql
  void parameterize(Statement statement)
      throws SQLException;
  
  // Add to the batch operation
  void batch(Statement statement)
      throws SQLException;
  
  // Perform the update operation
  int update(Statement statement)
      throws SQLException;
  
  // Perform the query and return the result
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;

  // Get the BoundSql object
  BoundSql getBoundSql(a);

  // Get the parameter handler
  ParameterHandler getParameterHandler(a);

}
Copy the code
2.2 Creating a StatementHandler Object

Where is StatementHandler created? In the Executor Executor of MyBatis, the StatementHandler object is created when the doQuery and doUpdate methods are executed. In the case of SimpleExecutor, the StatementHandler object is actually created by the Configuration object.

// SimpleExecutor doUpdate method
@Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      1. Create StatementHandler
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null.null);
      // 2. Create Statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 3. Perform the SQL operation
      return handler.update(stmt);
    } finally {
      // 2. Close StatementcloseStatement(stmt); }}Copy the code

In the newStatementHandler method of Configuration, the RoutingStatementHandler object is created. We know that the RoutingStatementHandler is actually a wrapper around the three Statementhandlers.

// The newStatementHandler method of Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
 // Load the plug-in
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
Copy the code

Click to create a specific StatementHandler object based on the type of the MappedStatement object. If MappedStatement does not specify a specific StatementType, StatementType defaults to PREPARED.

// The actual processor, one of the three processors: SimpleStatementHandler, PreparedStatementHandler, CallableStatementHandler
  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    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()); }}Copy the code

Next, let’s look at the PreparedStatementHandler creation process, which actually calls the BaseStatementHandler constructor.

// PreparedStatementHandler constructor
 public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
   // The BaseStatementHandler constructor is called
   super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
 }
Copy the code

At this point, we know that all three Statementhandlers are created using the BaseStatementHandler constructor.

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }
Copy the code
2.3 Creating a Statement Object

Once the StatementHandler object is created, you can create the Statement object. Again, take the SimpleExecutor executor as an example.

      1. Create StatementHandler
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null.null);
      // 2. Create Statementstmt = prepareStatement(handler, ms.getStatementLog()); .Copy the code

SimpleExecutor’s prepareStatement method does the following three steps:

  1. Getting a database connection
  2. Creating a Statement object
  3. Setting SQL Parameters
// The prepareStatement method of SimpleExecutor
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 1. Obtain the database connection
    Connection connection = getConnection(statementLog);
    2. Create a Statement object
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 3. Set SQL parameters
    handler.parameterize(stmt);
    return stmt;
  }
Copy the code

Moving on, let’s look at what the prepare() method does. This method BaseStatementHandler gives the default implementation, so all three Statementhandlers use this implementation. The main work is as follows

  1. Initialize the Statement object
  2. Setting timeout
  3. Set query size
  4. The Statement object is closed abnormally
// BaseStatementHandler prepares
 @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      // 1. Initialize the Statement object
      statement = instantiateStatement(connection);
      // 2. Set the timeout period
      setStatementTimeout(statement, transactionTimeout);
      // 3. Set the query size
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      // 4. Close the Statement object
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement. Cause: "+ e, e); }}Copy the code

The Statement object initialization is done in the instantiateStatement method, so let’s look at what the instantiateStatement method does. Ok, the instantiateStatement method in BaseStatementHandler is designed as an abstract method and implemented by subclasses, which also reflects the template method design pattern.

// instantiateStatement method of BaseStatementHandler
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
Copy the code

Now SimpleStatementHandler as example, the final call or connection. The createStatement () method, returned to the original starting point, namely MyBatis JDBC operations for the packaging.

@Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    / / is actually the call connection. The createStatement () method
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.createStatement();
    } else {
      returnconnection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); }}Copy the code

Once you get the Statement object, you can happily execute the execute method to send the SQL Statement to the database.

3. MappedStatement object

For those of you who are discerning, you will see that when Executor executes a doUpdate, it passes a MappedStatement object. What is the relationship between the MappedStatement object and the Statement and StatementHandler object? When we use MyBatis configuration in SQL, insert/update/delete/select the label below contains a SQL, MappedStatement is information description of SQL TAB.

// A select tag corresponds to an MappedStatement object<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from t_test_user
    where id = #{id,jdbcType=BIGINT}
  </select>
Copy the code

The MappedStatement class has the following private properties

// mapper Configuration file name, for example, usermapper. XML private String resource; // Private Configuration Configuration; / / namespace + id tags such as com. Example. Demo. Dao. TTestUserMapper. SelectByPrimaryKey private String id; private Integer fetchSize; Private Integer timeout; // SQL object type STATEMENT, PREPARED, CALLABLE private StatementType StatementType; // ResultSetType private ResultSetType ResultSetType; // SQL statement private SqlSource SqlSource; Private Cache Cache; Parameter mapping private ParameterMap ParameterMap; Private List<ResultMap> resultMaps; private List<ResultMap> resultMaps; private boolean flushCacheRequired; Private Boolean useCache; Private Boolean resultOrdered; // SQL statement type, INSERT, UPDATE, DELETE, SELECT private SqlCommandType SqlCommandType; private KeyGenerator keyGenerator; private String[] keyProperties; private String[] keyColumns; private boolean hasNestedResultMaps; private String databaseId; private Log statementLog; private LanguageDriver lang; private String[] resultSets;Copy the code

The main method is the getBoundSql method, which parses the dynamic SQL to get the final SQL statement. We will cover SqlSource and BoundSql in other articles.

  public BoundSql getBoundSql(Object parameterObject) {
    // Get the BoundSql object, which is a parse of dynamic SQL
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    // Get the parameter mapping
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if(rmId ! =null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if(rm ! =null) { hasNestedResultMaps |= rm.hasNestedResultMaps(); }}}return boundSql;
  }
Copy the code

4. To summarize

This article introduces the Statement object, StatementHandler and its three implementations, as well as the MappedStatement. As the main component of the Statement object, understanding its creation and principle will be very helpful for us to understand MyBatis.