Mybatis source code mybatis excel framework

As we introduced in the previous chapter, Mybatis will convert all database operations into iBatis programming model, and operate the database through...

As we introduced in the previous chapter, Mybatis will convert all database operations into iBatis programming model, and operate the database through the facade class SqlSession. However, when we go deep into the source code of SqlSession, we will find that SqlSession does nothing, and it entrusts you with the database operations, as shown in the figure below:

Excel framework class diagram

BaseExecutor

In BaseExecutor, the basic implementation of Executor is defined, such as query level 1 cache, transaction processing and other unchanging parts, operation database and other changing parts are implemented by subclasses, and template design mode is used. Let's look at the source code of query method:

@Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } /** * All query operations are finally handled by this method */ @SuppressWarnings("unchecked") @Override 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()) { // Clear local cache clearLocalCache(); } List<E> list; try { // Query level plus one queryStack++; // Query level 1 cache list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { // Handling OUT parameters of stored procedures handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // Cache misses, querying databases list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { // Query level minus one queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // Add cache placeholder localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { // Delete cache placeholder localCache.removeObject(key); } // Add query results to local cache localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { // Cache parameters if stored procedure localOutputParameterCache.putObject(key, parameter); } return list; }

In the queryFromDatabase() method, we can see that doQuery uses the template method, and the specific logic is implemented by the subclass. The advantage of doing this is that the subclass only cares about the part of the program changes, and the other unchanged parts are implemented by the parent class. It improves the reusability and expansibility of code.

SimpleExecutor

Common executor, which is used by default by Mybatis, to create a new Statement every time. Let's look at the source code of the query method:

@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { // Get Mybatis configuration class Configuration configuration = ms.getConfiguration(); // Get StatementHandler according to configuration class StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // Create Statement stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // Get Connection connection Connection connection = getConnection(statementLog); // Get Statement according to Connection stmt = handler.prepare(connection, transaction.getTimeout()); // Setting parameters handler.parameterize(stmt); return stmt; }

With stmt = handler.prepare(connection, transaction.getTimeout()); method, we can see that each time a new Statement is created.

ReuseExecutor

The executors that can be reused are statements. Internally, a Map is used to cache Statement objects with sql statements as the key. As long as the connection is kept open, Statement can be reused.

Because every new SqlSession has a new Executor object, the scope of the Statement we cache on reuseexecution is the same SqlSession, so in fact, this cache is not very useful. Let's take a look at getting the Statement source code directly. The other parts are the same as the simple Executor query method.

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; BoundSql boundSql = handler.getBoundSql(); String sql = boundSql.getSql(); if (hasStatementFor(sql)) { // Get reused Statement stmt = getStatement(sql); applyTransactionTimeout(stmt); } else { // Create a new Statement and cache it Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); putStatement(sql, stmt); } handler.parameterize(stmt); return stmt; } private boolean hasStatementFor(String sql) { try { // Judge whether the Statement is cached according to the sql and whether the Connection is closed return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed(); } catch (SQLException e) { return false; } } private Statement getStatement(String s) { return statementMap.get(s); } private void putStatement(String sql, Statement stmt) { statementMap.put(sql, stmt); }

BatchExecutor

Batch executor, which encapsulates the jdbc statement.addBatch(String sql) and statement.executeBatch(); to realize batch processing. Transactions for this actuator can only be in manual commit mode.

We usually perform batch processing in the way of sql splicing.

When performing batch update, it is recommended not to update too much data at a time. If the amount of updated data is large, it can be performed in sections.

CachingExecutor

If the second level cache is enabled, Mybatis will use the cachexecution executor, which uses the decorator mode.

@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // Get L2 cache Cache cache = ms.getCache(); if (cache != null) { // Refresh cache flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); // Cache lookup @SuppressWarnings("unchecked") List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { // Call decorated method list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // Put data in cache tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // Cache not found, call decorated method directly return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

Through the source code, we find that the whole query process has become L2 - > L1 - > dB. The query process of the caching executor increases the query operation of the secondary cache.

In practice, we seldom use the second level cache of Mybatis. If we want to do the second level cache, we recommend using the third-party cache framework directly at the service level. It is recommended to use the third-party cache framework Layered cache: a multi-level cache framework for monitoring It is more convenient and flexible to use. The query process is L1 - > L2 - > dB.

summary
  • BaseExecutor: the template method pattern is used to define the basic implementation of Executor. It is an abstract class and cannot provide services directly to the outside world.
  • Simple executor: a common executor, which is used by Mybatis by default. Each time a Statement is created.
  • Reuse executor: the executor of Statement can be reused, but the Statement cache is only valid in one SqlSession. We seldom perform the same query operations in one SqlSession, so the performance improvement is not great.
  • BatchExecutor: batch executor
  • Caching executor: second level cache executor. Using decorator mode, the whole query process becomes L2 - > L1 - > dB. It is recommended to use the third-party caching framework directly, such as: Layered cache: a multi-level cache framework for monitoring.

5 November 2019, 22:58 | Views: 5009

Add new comment

For adding a comment, please log in
or create account

0 comments