Take a look at the Page entity class definition for a few important attribute values.
public class Page<E> extends ArrayList<E> {
private static final long serialVersionUID = 1L;
/** * no count query */
private static final int NO_SQL_COUNT = -1;
/** * count */
private static final int SQL_COUNT = 0;
/** * page number, starting from 1 */
private int pageNum;
/** * page size */
private int pageSize;
/** * start line */
private int startRow;
/** ** last line */
private int endRow;
/** ** total */
private long total;
/** * Total pages */
private int pages;
/** ** ** */
private Boolean reasonable;
/** * When set to true, if pagesize is set to 0 (or RowBounds limit=0), no paging is performed and all results are returned */
private Boolean pageSizeZero;
}
Copy the code
Intercepts is the mybatis annotation, @signature is the method Signature interceptor Intercepts,
Class, Object. Class, RowBounds. Class, resulthandler.class
@Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
Copy the code
The implementation of Interceptor is to implement mybatis Interceptor interface
public class PageHelper implements Interceptor {
/ / SQL tools
private SqlUtil sqlUtil;
// Attribute parameter information
private Properties properties;
// Automatically obtain the dialect
private Boolean autoDialect;
}
Copy the code
PageHelper’s pagination method is straightforward and unnecessary.
/** * start paging **@paramPageNum page *@paramPageSize Number of displays per page */
public static Page startPage(int pageNum, int pageSize) {
return startPage(pageNum, pageSize, true);
}
/** * start paging **@paramPageNum page *@paramPageSize Number of displays per page *@paramCount Whether to perform count query */
public static Page startPage(int pageNum, int pageSize, boolean count) {
return startPage(pageNum, pageSize, count, null);
}
/** * start paging **@paramPageNum page *@paramPageSize Number of displays per page *@paramOrderBy sorting * /
public static Page startPage(int pageNum, int pageSize, String orderBy) {
orderBy(orderBy);
return startPage(pageNum, pageSize);
}
/** * start paging **@paramPageNum page *@paramPageSize Number of displays per page *@paramCount Whether to perform count query *@paramReasonable pagination is reasonable. When null, the default configuration */ is used
public static Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable) {
return startPage(pageNum, pageSize, count, reasonable, null);
}
/** * start paging **@paramPageNum page *@paramPageSize Number of displays per page *@paramCount Whether to perform count query *@paramReasonable pagination is reasonable. If null, default configuration * is used@paramPageSizeZero true and pageSize=0 returns all results, false returns pages, and null uses the default configuration */
public static Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page page = new Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
SqlUtil.setLocalPage(page);
return page;
}
/** * start paging **@param params
*/
public static Page startPage(Object params) {
Page page = SqlUtil.getPageFromObject(params);
SqlUtil.setLocalPage(page);
return page;
}
/** * sort **@param orderBy
*/
public static void orderBy(String orderBy) {
OrderByHelper.orderBy(orderBy);
}
Copy the code
The Interceptor from Mybatis is intercepted from the InnvocationHandler object in the JDK’s Invocation Invocation (Invocation.proceed());
/** * Mybatis interceptor method **@paramThe Invocation interceptor entry parameter *@returnReturns the execution result *@throwsThrowable throws an exception */
public Object intercept(Invocation invocation) throws Throwable {
if (autoDialect) {
initSqlUtil(invocation);
}
return sqlUtil.processPage(invocation);
}
Copy the code
It involves sqlUtil, with the Invocation. Proceed () wrapped in a layer. Let’s start with the initialization of sqlUtil.
In the following code, an important class MetaObject is provided by Mybatis for convenient and elegant access to object attributes, through which can simplify the code, do not need to try/catch various reflect exceptions. It also supports operations on javabeans, Collection, and Map objects.
MetaObject is used to obtain the DataSource attribute referenced by the MappedStatement object. The object reference chain is MappedStatement >Configuration >Environment > DataSource
After retrieving the datasource URL, encapsulate it in SqlUtil, and the initialization process is complete.
/** * initialize sqlUtil **@param invocation
*/
public synchronized void initSqlUtil(Invocation invocation) {
if (sqlUtil == null) {
String url = null;
try {
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
MetaObject msObject = SystemMetaObject.forObject(ms);
DataSource dataSource = (DataSource) msObject.getValue("configuration.environment.dataSource");
url = dataSource.getConnection().getMetaData().getURL();
} catch (SQLException e) {
throw new RuntimeException("Paging plug-in initialization exception :" + e.getMessage());
}
if (url == null || url.length() == 0) {
throw new RuntimeException("Unable to automatically obtain jdbcUrl, please configure the dialect parameter in the paging plug-in!");
}
String dialect = Dialect.fromJdbcUrl(url);
if (dialect == null) {
throw new RuntimeException("The database type cannot be automatically obtained. Please use the dialect parameter!");
}
sqlUtil = new SqlUtil(dialect);
sqlUtil.setProperties(properties);
properties = null;
autoDialect = false; }}Copy the code
The _processPage method is used after a look at a few member variables of the sqlUtil class, where the Page object and COUNT are stored in ThreadLocal to avoid concurrency problems.
private static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
private static final ThreadLocal<Boolean> COUNT = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue(a) {
return null; }};private static final Map<String, MappedStatement> msCountMap = new ConcurrentHashMap<String, MappedStatement>();
Copy the code
Back to see sqlUtil. ProcessPage (invocation) method calls, the final call is com. Making. Pagehelper. SqlUtil. _processPage method. The source code is too long, and the source code has Chinese annotations more intuitive, here just comb through the code logic, pseudo-code is as follows:
private Object _processPage(Invocation invocation) throws Throwable {
final Object[] args = invocation.getArgs();
RowBounds rowBounds = (RowBounds) args[2];
// Branch 1: If there is no page object LocalPage,RowBounds takes the default and does not perform paging
if (SqlUtil.getLocalPage() == null && rowBounds == RowBounds.DEFAULT) {
// If there is a sort requirement, sort interceptor is executed
if(OrderByHelper.getOrderBy() ! =null) {
OrderByHelper.processIntercept(invocation);
}
// Execute the original SQL without sorting
return invocation.proceed();
// Branch 2: paging is required
} else {
// Get the original ms
MappedStatement ms = (MappedStatement) args[0];
// Determine and process as PageSqlSource
if(! isPageSqlSource(ms)) { processMappedStatement(ms, parser); }// Ignore RowBounds- otherwise it will paging Mybatis
args[2] = RowBounds.DEFAULT;
// Paging information
Page page = getPage(rowBounds);
// Branch 2.1 to determine pageSizeZero, there is no need to perform paging
/ / pageSizeZero judgment
if((page.getPageSizeZero() ! =null && page.getPageSizeZero()) && page.getPageSize() == 0(Object result = Invocation. Proceed ();return page;
}
/ / branch 2.2
// Simply use the value of total to determine whether to run a count query
if (page.isCount()) {
COUNT.set(Boolean.TRUE);
/ / replace MS
args[0] = msCountMap.get(ms.getId());
// Query the total number
Object result = invocation.proceed();
/ / reduction of ms
args[0] = ms;
// Set the total number
page.setTotal((Integer) ((List) result).get(0));
if (page.getTotal() == 0) {
returnpage; }}/ / branch 2.3
PageSize >0; pageSize<=0; pageSize<=0
if (page.getPageSize() > 0 &&
((rowBounds == RowBounds.DEFAULT && page.getPageNum() > 0) || rowBounds ! = RowBounds.DEFAULT)) {// Replace SQL with paging SQL
BoundSql boundSql = ms.getBoundSql(args[1]);
args[1] = parser.setPageParameter(ms, args[1], boundSql, page);
COUNT.set(Boolean.FALSE);
// Perform paging queries
Object result = invocation.proceed();
// Get the processing result
page.addAll((List) result);
}
// Return the result
returnpage; }}Copy the code
To summarize, branch 1 does not perform paging and has no page-related variables in ThreadLocal at all. SQL > select count, mappedStatement, count from ThreadLocal, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement, mappedStatement Invocation. Proceed () Executes the original SQL; SQL returns paging query results. Paging or not, you end up with a Page object.
The last two PageHelper methods are also interface methods that rewrite the Interceptor.
The plugin() method is the method provided by Mybatis to generate proxy objects. See plugin.wrap (target, this) for details.
/** * only Executor ** is intercepted@param target
* @return* /
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
} else {
returntarget; }}Copy the code
SetProperties () reads property values from mybatis-config.xml. The following XML example reads the value value of the name attribute.
<plugins>
<plugin interceptor="com.example.springbootdemo.mybatis.interceptor.MybatisInterceptor">
<property name="name" value="test"></property>
</plugin>
</plugins>
Copy the code
Set the database type as follows:
<! After 4.0.0, this parameter can not be set -->
<property name="dialect" value="mysql" />
Copy the code
Public void setProperties(Properties p) {//MyBatis3.2.0 check try { Class.forName("org.apache.ibatis.scripting.xmltags.SqlNode"); } catch (ClassNotFoundException e) {throw new RuntimeException(" MyBatis is too late, MyBatis pagination plugin PageHelper supports MyBatis3.2.0 and above! ); } // Database dialect String dialect = p.getProperty("dialect"); if (dialect == null || dialect.length() == 0) { autoDialect = true; this.properties = p; } else { autoDialect = false; sqlUtil = new SqlUtil(dialect); sqlUtil.setProperties(p); }}}Copy the code