This is the 15th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Pagehelper-spring-boot-starter pageHelper-spring-boot-starter pageHelper-spring-starter Learned PageHelper is by implementing MyBatis Interceptor interface org. Apache. Ibatis. Plugin. The Interceptor PageInterceptor class so as to realize to rewrite the SQL.

How does the PageInterceptor rewrite SQL?

We know that interceptors are generally implemented through reflection mechanisms. (This article will not expand on).

The most important method in an interceptor is usually called an Intercept and is usually in the form of code like this:

@Override
    public Object intercept(Invocation invocation) {
        // before the logical code
        invocation.invoke(); // Execute the actual intercepted code
        // After the logical code
    }
Copy the code

Invocation. Invoke () is the invocation of the interface code or SQL so that the invocation can be modified globally.

Let’s take a look at the PageInterceptor interceptor code:

@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;
            // Only one entry will be entered due to logic
            if (args.length == 4) {
                //4 arguments
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                //6 parameters
                cacheKey = (CacheKey) args[4];
                boundSql = (BoundSql) args[5];
            }
            checkDialectExists();
            // Intercepting boundSql
            if (dialect instanceof BoundSqlInterceptor.Chain) {
                boundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.ORIGINAL, boundSql, cacheKey);
            }
            List resultList;
            // Call the method to determine whether paging is required, and return the result if not
            if(! dialect.skip(ms, parameter, rowBounds)) {// Determine whether a count query is required
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    // Query the total number
                    Long count = count(executor, ms, parameter, rowBounds, null, boundSql);
                    // Process the total number of queries, return true to continue paging query, false directly return
                    if(! dialect.afterCount(count, parameter, rowBounds)) {// If the total number of queries is 0, an empty result is returned
                        return dialect.afterPage(newArrayList(), parameter, rowBounds); } } resultList = ExecutorUtil.pageQuery(dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);  }else {
                //rowBounds takes the parameter value and supports the default memory paging when not handled by the paging plug-in
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            if(dialect ! =null){ dialect.afterAll(); }}}Copy the code

There’s a lot of code, so let’s break it down line by line:

Get the arguments in interception:

Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
Copy the code

The types are MappedStatement, Object, RowBounds, and ResultHandler. To make things more clear, let’s briefly list the structure

  1. MappedStatement (resource, configuration, id, fetchSize, timeout, statementType, resultSetType, sqlSource, cache, parameterMap, resultMaps, … , sqlCommandType, keyGenerator, keyProperties, keyColumns, databaseId, statementLog, LanguageDriver, resultSets);
  2. Object, do not do;
  3. RowBounds: offset, limit, (there are constants NO_ROW_OFFSET=0, NO_ROW_LIMIT = 2147483647);
  4. ResultHandler<T>There is only one abstract method:
public abstract  void handleResult(org.apache.ibatis.session.ResultContext<? extends T> arg0);

Copy the code

Where are these parameters intercepted? Or rather, what the hell are we intercepting?

PageHelper wiki documentation gives the answer: org. Apache. Ibatis. Executor. Executor.

Wiki:

In the documentation section of MyBatis’ interceptor, we know that query methods in Executor can be intercepted

The form of the query method is:

<E> List<E> query( MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
Copy the code

[Manual dog head] look, is it corresponding to the 4 parameters!

Who invented the Springboot interface and implementation mode, the source code to see similar, a rumble or miss a lot of source code.