简介
PageHelper可以很大程度减少在使用MyBatis PageHelper插件的时候。但是使用的时候发现,执行发现不止第一个sql被分页了,后面的也会有sql被执行了分页,顾去阅读PageHelper的源码查看原因。
一、直接说无效的原因
该项目中 MyBatis 的mapper接口传入的参数如下,所有的前端传过来的搜索条件对象都继承了这个类。
1
2
3
4
5
6public class BaseSearchVo {
private Integer pageNum = 1;
private Integer pageSize = 10;
private String orderBy;
// setter getter...
}PageHelper参数设置了
pagehelper.supportMethodsArguments = true
当该参数设置为true的时候,PageHelper会检查出入sql的参数有没有 pageNum 和 pageSize 两个参数,如果有,则按照两个参数进行分页,等效于执行了PageHelper.startPage方法。
二、PageHelper源码 分页基本原理
PageHelper 主要通过分页拦截器 PageInterceptor 实现,基本的代码结构如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class PageInterceptor implements Interceptor {
// properties...
// 每个sql都会通过拦截器
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
// 获取方法的参数
Object[] args = invocation.getArgs();
// 省略处理参数代码
//调用方法判断是否需要进行分页,如果不需要,直接返回结果,skip方法查看下一段代码
if (!dialect.skip(ms, parameter, rowBounds)) {
// 分页处理sql
} else {
// 直接执行sql
}
} finally {
// 每次sql执行完成都会执行 clearPage 的操作,所以PageHelper只会对静态方法PageHelper.startPage(1, 10)方法后的第一个sql拦截,然后分页。
dialect.afterAll();
}
}
判断是否需要分页的方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@Override
public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
if(ms.getId().endsWith(MSUtils.COUNT)){
throw new RuntimeException("在系统中发现了多个分页插件,请检查系统配置!");
}
// 主要判断方法!!!如果返回null,说明不需要分页,具体getPage方法见下一段代码
Page page = pageParams.getPage(parameterObject, rowBounds);
if (page == null) {
return true;
} else {
//设置默认的 count 列
if(StringUtil.isEmpty(page.getCountColumn())){
page.setCountColumn(pageParams.getCountColumn());
}
autoDialect.initDelegateDialect(ms);
return false;
}
}
pageParams.getPage()方法如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24# class -> com.github.pagehelper.page.PageParams
public Page getPage(Object parameterObject, RowBounds rowBounds) {
// 获取通过 PageHelper.startPage 设置的page对象,如果没有设置即为null
Page page = PageHelper.getLocalPage();
if (page == null) {
if (rowBounds != RowBounds.DEFAULT) {
// 省略与本次无关的代码
} else if(supportMethodsArguments){
try {
// 重点来了,如果设置 supportMethodsArguments 参数为 true, 会尝试从参数中获取page对象
// 具体方法见下一段代码
page = PageObjectUtil.getPageFromObject(parameterObject, false);
} catch (Exception e) {
return null;
}
}
if(page == null){
return null;
}
PageHelper.setLocalPage(page);
}
// 省略部分代码...
return page;
}
PageObjectUtil.getPageFromObject 方法如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53// 方法通过反射获取pageNum与pageSize的值
public static <T> Page<T> getPageFromObject(Object params, boolean required) {
int pageNum;
int pageSize;
MetaObject paramsObject = null;
if (params == null) {
throw new PageException("无法获取分页查询参数!");
}
// 省略部分代码...
try {
// 获取参数的方法!!!
Object _pageNum = getParamValue(paramsObject, "pageNum", required);
Object _pageSize = getParamValue(paramsObject, "pageSize", required);
if (_pageNum == null || _pageSize == null) {
if(hasOrderBy){
Page page = new Page();
page.setOrderBy(orderBy.toString());
page.setOrderByOnly(true);
return page;
}
return null;
}
pageNum = Integer.parseInt(String.valueOf(_pageNum));
pageSize = Integer.parseInt(String.valueOf(_pageSize));
} catch (NumberFormatException e) {
throw new PageException("分页参数不是合法的数字类型!");
}
// 如果获取参数没有异常,则构建一个新的page对象设置到本地,类似于进行了PageHelper.startPage操作
Page page = new Page(pageNum, pageSize);
// 省略部分代码...
return page;
}
/**
* 从对象中取参数
*/
protected static Object getParamValue(MetaObject paramsObject, String paramName, boolean required) {
Object value = null;
if (paramsObject.hasGetter(PARAMS.get(paramName))) {
value = paramsObject.getValue(PARAMS.get(paramName));
}
if (value != null && value.getClass().isArray()) {
Object[] values = (Object[]) value;
if (values.length == 0) {
value = null;
} else {
value = values[0];
}
}
if (required && value == null) {
throw new PageException("分页查询缺少必要的参数:" + PARAMS.get(paramName));
}
return value;
}
总结
此次问题的主要原因是本地业务对象中的参数 pageNum 和 pageSize 和PageHelper中的分页参数命名完全一样,被反射获取到了,同时显示的设定了 supportMethodsArguments = true // 默认为false,导致只要使用到了这个搜索对象的地方都会进行分页操作,即使没有调用PageHelper.startPage 方法。