preface

  • This is the fourth special article of Mybatis. The first three articles talked about the basic use of Mybatis. I believe that as long as the friends who read it carefully, the normal use of Mybatis should not be a problem in actual development. Mybatis basic operation, Mybatis result mapping, you shoot accurate? , Mybatis dynamic SQL, you really can? .
  • Of course, any technology can not be shallow hidden at every stop, today the author will take you deep into the underlying source code to see the infrastructure of Mybatis. This article is just an introduction to the source code, talking about some important components in Mybatis, which the author callsSix swordsman.

Environment version

  • Everything in this article is based onMybatis3.5andSpringBoot - 2.3.3. RELEASE.

The six swordsmen of Myabtis

  • In fact, the underlying source code of Mybatis is very easy to read compared with Spring. The author picks out six important interfaces and calls themSix Swordsmen of Mybatis, respectively,SqlSession,Executor,StatementHandler,ParameterHandler,ResultSetHandler,TypeHandler.
  • What are the roles of the six swordsmen in Mybatis? Each of these will be described below.
  • Before introducing six Swordsmen, here is a flow chart of six Swordsmen execution, as follows:

SqlSession

  • SqlSession is the core API in Myabtis, mainly used to execute commands, obtain mappings, and manage transactions. It contains all methods for executing statements, committing or rolling back transactions, and getting mapper instances.

What is the method

  • Nearly 20 methods are defined, including statement execution, transaction commit rollback and so on. These methods are summarized in the following categories.

Statement execution method

  • These methods are used to execute SELECT, INSERT, UPDATE, and DELETE statements defined in THE SQL mapping XML file. You can get a quick idea of what they do by name. Each method takes the ID of a statement and a parameter object, which can be a primitive type (supporting auto-boxing or wrapping classes), JavaBean, POJO, or Map.
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
Copy the code
  • One of the most misunderstood of allselectOneandselectList, it is easy to know the difference from the method name, one is to query a single, one is to query multiple. This is recommended if you are not sure if your SQL will return one or more resultsselectList.
  • insert.update.deleteThe method backvalue is the number of rows affected.
  • Select also has several reusable methods to limit the number of rows returned, which in Mysql correspond to thislimitThat is as follows:
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)
Copy the code
  • One of theRowBoundsThe limit number of rows, the start number of rows, is saved in the parameter.

The immediate batch update method

  • BATCH When you set ExecutorType to executorType. BATCH, you can use this method to clear (execute) BATCH update statements cached in JDBC driver classes.
List<BatchResult> flushStatements(a)
Copy the code

Transaction control method

  • There are four methods for controlling transaction scope. Of course, these methods won’t work if you’ve set up automatic commit or if you use an external transaction manager. However, if you are using a JDBC transaction manager controlled by a Connection instance, these four methods will come in handy:
void commit(a)
void commit(boolean force)
void rollback(a)
void rollback(boolean force)
Copy the code
  • By default, MyBatis does not commit transactions automatically unless it detects that the database has been changed by calling the insert, update, or delete methods. If you don’t use these methods to submit changes, you can do so in thecommitrollbackMethod argument to ensure that the transaction is committed properly (note that this is set in auto-commit mode or if an external transaction manager is used)forceThe value ofsessionInvalid). Most of the time you don’t have to call itrollback()Because MyBatis will be called in your absencecommitTo complete the rollback operation for you. However, when you have detailed control over transactions in a session that can be committed or rolled back multiple times, rollback operations come in handy.

Local cache method

  • Mybatis uses two types of caches: local cache and second level cache.
  • By default, the lifetime of locally cached data is equal to the lifetime of the entire session. Because caching is used to solve circular reference problems and speed up repeated nested queries, it cannot be completely disabled. But you can do it by settinglocalCacheScope=STATEMENTTo use the cache only during statement execution.
  • You can call the following methods to clear the local cache.
void clearCache(a)
Copy the code

Fetch mapper

  • You can also get your own mapper in SqlSession using the following method:
<T> T getMapper(Class<T> type)
Copy the code
  • Let’s say you need to get oneUserMapperThat is as follows:
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
Copy the code

What implementation classes are available

  • There are three implementation classes in Mybatis, respectivelyDefaultSqlSession.SqlSessionManager,SqlSessionTemplateOne of the most important things is thatDefaultSqlSession, this will be analyzed when Mybatis executes the source code.
  • Mybatis’ launcher configuration class is injected by default when integrated with SpringBootSqlSessionTemplate, the source code is as follows:
@Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) 
    // Create different actuators based on the type of actuators, CachingExecutor by default
    ExecutorType executorType = this.properties.getExecutorType();
    if(executorType ! =null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return newSqlSessionTemplate(sqlSessionFactory); }}Copy the code

Executor

  • The executer of Mybatis is the scheduling core of Mybatis, responsible for the generation of SQL statements and the maintenance of cache. Crud methods in SqlSession are actually executed by calling corresponding methods in the executer.
  • The inheritance structure is shown below:

The implementation class

  • Let’s take a look at which implementation classes are available and what they do.

BaseExecutor

  • This is an abstract class, using the template method model, and it’s interesting that this brother mimics Spring’s way, the real implementation of methods aredoxxx().
  • There is one method worth noting, query time to goLevel 1 cacheMybatis selects a level cache by default, since this is a template class. The code is as follows:
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    LocalCache is a level 1 cache, which is a Map structure
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // Perform the real query
      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

CachingExecutor

  • This is a little bit more famous, the maintenance class for level 2 cache, which is created by default by integrating with SpringBoot. Let’s look at how to go to the secondary cache, the source code is as follows:
@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // Check whether the current Sql is using level 2 cache
    Cache cache = ms.getCache();
    // Use the cache, directly from the cache
    if(cache ! =null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        // Fetch data from the cache
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          SQL > select * from database
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // Add it to cache
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        // Return directly
        returnlist; }}// Execute SQL query directly from database
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
Copy the code
  • It’s just a level-two cache, nothing else.

SimpleExecutor

  • This class is like a straight male, the simplest of the actuators, is executed according to the corresponding SQL, no additional operations.

BatchExecutor

  • Optimize performance through batch operations. It’s usually worth noting thatBatch updateOperation, because of internal cache implementation, remember to call after useflushStatementsTo clear the cache.

ReuseExecutor

  • An executable that can be reused. The object to be reused is a Statement, which means that the executable will cache the same SQLStatementTo save the creation of Statement and optimize performance.
  • The internal implementation is through aHashMapTo maintain the Statement object. Since the current Map is only valid in this session, remember to call it after using itflushStatementsTo clear the Map.

How to create in SpringBoot

  • Which executor is created in SpringBoot? In fact, as long as you read the source code can be very clear, the answer is inorg.apache.ibatis.session.ConfigurationClass, which create the source of the actuator is as follows:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // The type of the executor is not specified. The default is SimpleExecutor
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // The type is BATCH, which creates BatchExecutor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
      REUSE creates ReuseExecutor
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      // All but the above two are created as SimpleExecutor
      executor = new SimpleExecutor(this, transaction);
    }
    // If a cache level 2 is configured globally, CachingExecutor is created. This parameter is true by default in SpringBoot and can be set to false
    if (cacheEnabled) {
    / / create CachingExecutor
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
Copy the code
  • Obviously, the default created in SpringBoot isCachingExecutorBecause the defaultcacheEnabledThe value oftrue.

StatementHandler

  • If you are familiar with JDBC, you can probably guess what this interface does. It is obviously used to process SQL statements and assign parameters.

The implementation class

  • This interface also has a number of implementation classes, as shown below:

SimpleStatementHandler

  • The Statement interface is used for simple SQL processing

PreparedStatementHandler

  • This corresponds to PreparedStatement, the interface for pre-compiling SQL in JDBC.

CallableStatementHandler

  • This corresponds to a JDBC CallableStatement, which is used to execute the stored procedure related interface.

RoutingStatementHandler

  • This interface is the route for the three interfaces above. It is responsible for creating and calling the three StatementHandler.

ParameterHandler

  • ParameterHandlerMybatis is responsible for replacing placeholders in SQL with real parameters. It is an interface with one and only one implementation classDefaultParameterHandler.
  • setParametersIs the core method of handling parameters. I’m not going to go into detail here, but I’ll talk about it later.

TypeHandler

  • The Java type and JDBC type are converted to each other during precompilation, setting parameters, and fetching results. Of course, Mybatis built-in a lot of default type processor, basically enough, unless there is a special customization, we will go to customize, such as the need to Java objects toJSONThe string is stored in the database, at which point you can customize a type handler.
  • Very simple stuff, I won’t go into detail here, a separate article on how to customize type handlers will follow.

ResultSetHandler

  • The result handler, which converts the ResultSet object returned by JDBC into a collection of type List orCursor.
  • The concrete implementation class isDefaultResultSetHandler, the implementation steps are to encapsulate the result set after the Statement is executed into corresponding objects according to the ResultType or ResultMap configured in the Mapper file, and finally return the encapsulated objects.
  • Source code and its complex, especially on the nested query of the analysis, here only do an understanding, the follow-up will write a special article.

conclusion

  • So far, the first Mybatis source code has been finished, this article on Mybatis important components to do a preliminary understanding, for the back of the more in-depth source code reading made a paving, if feel the author wrote good, in the look to share a wave, thank you for your support.