在 PageHelper官网,对PageHelper进行了如下描述
第一步:添加maven依赖
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.3
第二步:添加properties配置
#pagehelper分页插件配置
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql
第三步:在你的查询语句之前进行分页处理
//第几页
Integer pageNum = 1;
//一页多少数据
Integer pageSize = 10;
//开始分页
PageHelper.startPage(pageNum,pageSize);
//查询语句
studentService.selectAll();
通过以上三步,就已经实现了分页处理了。
那么,PageHelper是如何实现分页处理的呢?
第一步:我们首先得理解mybatis的plugin,可以在mybatis官方文档当中,找到如下描述
那么,我们可否实现自己的plugin,来打印需要执行的sql语句呢?
可以
比如我们先按照文档,写一个MyPlugin
@Intercepts({@Signature(
type= StatementHandler.class,
method = "prepare",
args = {Connection.class,Integer.class})})
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler= (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
//打印sql语句
System.out.println("MyPlugin" + boundSql.getSql());
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target,this);
}
@Override
public void setProperties(Properties properties) {
String dialect = properties.getProperty("dialect");
System.out.println("dialect="+dialect);
}
}
然后在springboot当中注册成Bean
@Bean
public MyPlugin myPlugin(){
MyPlugin myPlugin = new MyPlugin();
Properties properties = new Properties();
properties.setProperty("dialect","mysql");
myPlugin.setProperties(properties);
return myPlugin;
}
通过上面的两步我们就实现了一个自己的Plugin,来打印出需要执行的sql语句
第二步:通过上面文档的描述,我们知道,mybatis的插件就像springmvc当中的Interceptor,可以对方法进行拦截。那么既然可以对方法进行拦截,那么我可不可以,在每个查询方法之前,先检测一下当前语句是否需要分页,假如需要分页,我就拼接分页的SQL语句,然后再执行拼接后的SQL语句呢?
可以,因为PageHelper就是这样做的
在PageHelper分页当中,首先要执行startPage方法
//通过startPage,把当前的分页信息保存到ThreadLocal里面,确保每个线程之间互不影响
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);
Page oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
//把信息保存到ThreadLocal里面
setLocalPage(page);
return page;
}
然后可以查看到com.github.pagehelper.PageInterceptor,一个PageHelper的Plugin。
我们在PageInterceptor.intercept这个拦截方法当中,可以找到如下两个语句
String pageSql = this.dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey);
BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
其中,getPageSql根据每种数据库而不同,以MySQL数据库为例,它的getPageSql语句如下:
public String getPageSql(String sql, Page page, CacheKey pageKey) {
StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
sqlBuilder.append(sql);
//在这里,进行分页语句拼接
if (page.getStartRow() == 0) {
sqlBuilder.append(" LIMIT ? ");
} else {
sqlBuilder.append(" LIMIT ?, ? ");
}
pageKey.update(page.getPageSize());
return sqlBuilder.toString();
}
到此,我们就知道了PageHelper如何分页
1、通过startPage方法,把当前的分页参数保存到ThreadLocal
2、通过com.github.pagehelper.PageInterceptor拦截要执行的query方法
3、根据dialect信息,执行不同数据库的getPageSql方法,拼接分页语句
4、拦截器执行getPageSql返回的新的sql语句,进行分页查询