在 PageHelper官網,對PageHelper進行了如下描述
如何在Spring Boot項目當中引入PageHelper進行分頁處理呢?
第一步:添加maven依賴
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
第二步:添加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是如何實現分頁處理的呢?
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 <E> Page<E> startPage(int pageNum, int pageSize,
boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page<E> page = new Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
Page<E> 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<Page> LOCAL_PAGE這個參數當中
2、通過com.github.pagehelper.PageInterceptor攔截要執行的query方法
3、根據dialect信息,執行不同數據庫的getPageSql方法,拼接分頁語句
4、攔截器執行getPageSql返回的新的sql語句,進行分頁查詢