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 calls
Six swordsman
.
Environment version
- Everything in this article is based on
Mybatis3.5
andSpringBoot - 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 them
Six 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 all
selectOne
andselectList
, 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
.delete
The 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 this
limit
That 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 the
RowBounds
The 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 the
commit
和rollback
Method 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)force
The value ofsession
Invalid). Most of the time you don’t have to call itrollback()
Because MyBatis will be called in your absencecommit
To 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 setting
localCacheScope=STATEMENT
To 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 one
UserMapper
That is as follows:
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
Copy the code
What implementation classes are available
- There are three implementation classes in Mybatis, respectively
DefaultSqlSession
.SqlSessionManager
,SqlSessionTemplate
One 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 SpringBoot
SqlSessionTemplate
, 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 are
doxxx()
. - There is one method worth noting, query time to go
Level 1 cache
Mybatis 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 that
Batch update
Operation, because of internal cache implementation, remember to call after useflushStatements
To 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 SQL
Statement
To save the creation of Statement and optimize performance. - The internal implementation is through a
HashMap
To maintain the Statement object. Since the current Map is only valid in this session, remember to call it after using itflushStatements
To 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 in
org.apache.ibatis.session.Configuration
Class, 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 is
CachingExecutor
Because the defaultcacheEnabled
The 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
ParameterHandler
Mybatis is responsible for replacing placeholders in SQL with real parameters. It is an interface with one and only one implementation classDefaultParameterHandler
.setParameters
Is 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 to
JSON
The 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 or
Cursor
. - The concrete implementation class is
DefaultResultSetHandler
, 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.