Notes on PageHelper plug-in
-
PageHelperAutoConfiguration
Class notes:-
@Configuration
: equivalent to<beans></beans>
label -
@ConditionalOnBean(SqlSessionFactory.class)
: exists only in contextSqlSessionFactory
Ofbean
That’s how it worksPageHelperAutoConfiguration
-
@EnableConfigurationProperties(PageHelperProperties.class)
:@ConfigurationProperties
Annotations are mainly used toproperties
Profile tobean
To use, and@EnableConfigurationProperties
What is the function of annotation@ConfigurationProperties
The annotation takes effect. If you only configure@ConfigurationProperties
Notes, inIOC
Can’t get in containerproperties
Transformation of configuration filebean
Yes. -
@ConfigurationProperties(prefix = PageHelperProperties.PAGEHELPER_PREFIX)
:PageHelperProperties
yesPageHelper
The configuration class of theproperties
The configuration file in is converted toPageHelperProperties
Object,PageHelperProperties
It’s on the table@ConfigurationProperties(prefix = "PageHelperProperties.PAGEHELPER_PREFIX")
Notes. -
@AutoConfigureAfter(MybatisAutoConfiguration.class)
: whenMybatisAutoConfiguration
Load this class after loading this class -
@PostConstruct
This annotation is used to modify a non staticvoid()
method. cover@PostConstruct
The modified method will be loaded on the serverServlet
And will only be executed once by the server. Execution sequence:Constructor
(construction method) – >@Autowired
(dependency injection) – >@PostConstruct
(method of annotation)
-
-
PageInterceptor
Class notes@Intercepts({@Signature( type \= Executor.class, method \= "query", args \= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} ), @Signature( type \= Executor.class, method \= "query", args \= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class} )})
-
type
: represents the intercepted class, here isExcutor
Implementation class of -
method
: means the method of interception. Here is the method of interceptionExecutor
Ofquery
method -
args
: represents method parameters
Principle analysis of mybatis interceptor
Mybatis
Plug in provided(Plugin
)Function is actually interceptor function. By default,Mybatis
Plug ins are allowed to intercept method calls, including the following:
1. Executor (update, query, flushstatements, commit, rollback, gettransaction, close, isclosed) // method of intercepting executor
2. Parameterhandler (getparameterobject, setparameters) // method of intercepting parameters
3. Resultsethandler (handleresultsets, handleoutputparameters) // method of intercepting result set
4. Statementhandler (prepare, parameterize, batch, update, query) // intercepts the processing of SQL syntax construction
PageHelper auto configuration
pageHelper
Auto configuration class name for:PageHelperAutoConfiguration
@Configuration
@ConditionalOnBean(SqlSessionFactory.class)
@EnableConfigurationProperties(PageHelperProperties.class)
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class PageHelperAutoConfiguration {
@Autowired
private List<SqlSessionFactory\> sqlSessionFactoryList;
@Autowired
private PageHelperProperties properties;
/\*\*
\*Accept additional properties of the paging plug-in
\*
\* @return
\*/
@Bean
@ConfigurationProperties(prefix \= PageHelperProperties.PAGEHELPER\_PREFIX)
public Properties pageHelperProperties() {
return new Properties();
}
@PostConstruct
public void addPageInterceptor() {
PageInterceptor interceptor \= new PageInterceptor();
Properties properties \= new Properties();
//First, put in the properties configured in the general way
properties.putAll(pageHelperProperties());
//When the special configuration is put in, because close conn uses the above method, the attribute name is close conn instead of closeconn, so an extra step is needed
properties.putAll(this.properties.getProperties());
interceptor.setProperties(properties);
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
}
}
}
After the auto configuration construction method is executed, theaddPageInterceptor
Method, configure the property injection, andPageInterceptor
Instance added toConfiguration
Examples ofinterceptorChain
in
Mybatis component relationships
Mybatis
The core components of the system are as follows:
- SqlSessionAs the main top-level API of mybatis, it represents the interaction session with the database and completes the necessary database addition, deletion, modification and query functions
- ExecutorMybatis executor is the core of mybatis scheduling, responsible for SQL statement generation and query cache maintenance
- StatementHandlerIt encapsulates the JDBC statement operation and is responsible for the operation of JDBC statement, such as setting parameters and converting statement result set into list set.
- ParameterHandlerIt is responsible for converting the parameters passed by users into the parameters required by JDBC statement,
- ResultSetHandlerIt is responsible for converting the resultset object returned by JDBC into a list type collection;
- TypeHandlerResponsible for the mapping and conversion between Java data type and JDBC data type
- MappedStatementMappedstatement maintains an encapsulation of < select | update | delete | Insert > nodes,
- SqlSourceIt is responsible for dynamically generating SQL statements according to the parameterobject passed by the user, encapsulating the information into the boundsql object, and returning the
- BoundSqlRepresents the dynamically generated SQL statement and the corresponding parameter information
- ConfigurationAll configuration information of mybatis is maintained in the configuration object.
Mybatis execution process description
public static void main(String\[\] args) throws Exception {
/\*
\*1. Load mybatis configuration file, initialize mybatis, and create sqlsession factory, which is the factory to create sqlsession
\*This is just for the sake of demonstration. Sqlsessionfactory is created temporarily. In actual use, sqlsessionfactory only needs to be created once and used as a singleton
\*/
InputStream inputStream \= Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactoryBuilder builder \= new SqlSessionFactoryBuilder();
SqlSessionFactory factory \= builder.build(inputStream);
//2. Create a sqlsession from the sqlsession factory to operate the database
SqlSession sqlSession \= factory.openSession();
}
public SqlSession openSession() {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx \= null;
DefaultSqlSession var8;
try {
Environment environment \= this.configuration.getEnvironment();
TransactionFactory transactionFactory \= this.getTransactionFactoryFromEnvironment(environment);
tx \= transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor \= this.configuration.newExecutor(tx, execType);
var8 \= new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
You can see from the codeSqlSession
BySqlSessionFactory
OfoppenSession()
Method, and the process of generating the session willConfiguration
ClassnewExecutor
Method to create an actuator.
Mybatis
ActuatorExecutor
according toSqlSession
Parameter execution passedquery
Method. After a series of twists and turns, aStatementHandler
Object, and then pass the necessary parameters to theStatementHandler
, usingStatementHandler
To complete the query of the database, and finally return to theList
Result set
StatementHandler
The main tasks are as follows
- about
JDBC
OfPreparedStatement
In the process of creation, we useSQL
The statement string will contain several? Placeholders, and we will set the value of the placeholders later. -
StatementHandler
adoptList<E> query(Statement statement, ResultHandler resultHandler)
Method to complete the executionStatement
, and willStatement
ObjectresultSet
Package intoList
;
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler \= mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler \= (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler \= new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler \= (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
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);
statementHandler \= (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType \= executorType \== null ? defaultExecutorType : executorType;
executorType \= executorType \== null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH \== executorType) {
executor \= new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE \== executorType) {
executor \= new ReuseExecutor(this, transaction);
} else {
executor \= new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor \= new CachingExecutor(executor);
}
executor \= (Executor) interceptorChain.pluginAll(executor);
return executor;
}
FromMybatis
OfSQL
In the execution flow chart, you canMybatis
The four major objects ofExecutor
、ParameterHandler
、ResultSetHandler
、StatementHandler
And they work togetherSQL
Who created these four objects? Yes, it isMybatis
Configuration center for:Configuration
, create the source code as above:
These methods will eventually be calledinterceptorChain.pluginAll
Method, which refers to thePlugin
Ofwrap
method
public static Object wrap(Object target, Interceptor interceptor) {
//Get the method of @ signature in the intercepts annotation of pageinterceptor and store it in the signaturemap of plugin
Map<Class<?>, Set<Method\>> signatureMap \= getSignatureMap(interceptor);
Class<?> type \= target.getClass();
//Get all the interfaces implemented by the target object; the four objects are interfaces with default subclass implementation
//JDK's dynamic proxy only supports interfaces
Class<?>\[\] interfaces \= getAllInterfaces(type, signatureMap);
if (interfaces.length \> 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
pluginAll
In fact, it is to create agents for four major objects, oneInterceptor
A layer of agents will be created, and ourPageInterceptor
It’s just one of the agents; let’s move on,Plugin
InheritedInvocationHandler,JDK
Dynamic proxy, then itsinvoke
Method will definitely be called
@Override
public Object invoke(Object proxy, Method method, Object\[\] args) throws Throwable {
try {
//Get the method of @ signature in the intercepts annotation of pageinterceptor
Set<Method\> methods \= signatureMap.get(method.getDeclaringClass());
//When methods contains the target method, call the intercept method of pageinterceptor to complete the paging of SQL
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
You can see from the code that the last call has been made.PageInterceptor
Design of interceptorintercept
method
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
Object\[\] args \= invocation.getArgs();
MappedStatement ms \= (MappedStatement) args\[0\];
Object parameter \= args\[1\];
RowBounds rowBounds \= (RowBounds) args\[2\];
ResultHandler resultHandler \= (ResultHandler) args\[3\];
Executor executor \= (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//Because of the logical relationship, it will only enter once
if(args.length \== 4){
//4 parameters
boundSql \= ms.getBoundSql(parameter);
cacheKey \= executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 parameters
cacheKey \= (CacheKey) args\[4\];
boundSql \= (BoundSql) args\[5\];
}
List resultList;
//Call the method to determine whether pagination is needed. If not, directly return the result
//The page information will be taken from the current thread here. Do you remember when and where to put the page information into the current thread?
if (!dialect.skip(ms, parameter, rowBounds)) {
//Get dynamic parameters by reflection
String msId \= ms.getId();
Configuration configuration \= ms.getConfiguration();
Map<String, Object\> additionalParameters \= (Map<String, Object\>) additionalParametersField.get(boundSql);
//Judge whether count query is needed
if (dialect.beforeCount(ms, parameter, rowBounds)) {
String countMsId \= msId + countSuffix;
Long count;
//First judge whether there is a handwritten count query
MappedStatement countMs \= getExistedMappedStatement(configuration, countMsId);
if(countMs != null){
count \= executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
} else {
countMs \= msCountMap.get(countMsId);
//Automatic creation
if (countMs \== null) {
//Create a MS with a return value of long type according to the current Ms
countMs \= MSUtils.newCountMappedStatement(ms, countMsId);
msCountMap.put(countMsId, countMs);
}
count \= executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
}
//Total number of queries processed
//When it returns true, it will continue paging query, and when it returns false, it will return directly
if (!dialect.afterCount(count, parameter, rowBounds)) {
//When the total number of queries is 0, empty results are returned directly
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
}
}
//Determine whether pagination query is needed
if (dialect.beforePage(ms, parameter, rowBounds)) {
//Generate paged cache key
CacheKey pageKey \= cacheKey;
//Processing parameter objects
parameter \= dialect.processParameterObject(ms, parameter, boundSql, pageKey);
//Call dialect to get paging SQL
String pageSql \= dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
BoundSql pageBoundSql \= new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
//Setting dynamic parameters
for (String key : additionalParameters.keySet()) {
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//Perform paging query
resultList \= executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
} else {
//If paging is not performed, memory paging is not performed
resultList \= executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
}
} else {
//Rowbounds still supports the default memory paging when the parameter value is used and the paging plug-in is not used
resultList \= executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
return dialect.afterPage(resultList, parameter, rowBounds);
} finally {
dialect.afterAll();
}
}
It reads thePage
Information, according toPage
Information to determine whether pagination is needed; andPage
Information is stored in the current thread from our business code
References & thanks
- https://www.cnblogs.com/youzhibing/p/9603149.html
- https://blog.csdn.net/luanlouis/article/details/40422941
- https://juejin.im/post/5d085229f265da1bd605a629