1.简介
2.Spring Boot集成
2.1 pom配置
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>latest version</version>
</dependency>
2.2 Config PageHelper
1.Using in mybatis-config.xml
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- config params as the following -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
2.Using in Spring application.xml
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- config params as the following -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
3.Using in Spring Boot
pagehelper.helper-dialect=mysql
3.使用方法
3.1 方法1 RowBounds 和 PageRowBounds
List<User> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));
使用这种调用方式时,你可以使用RowBounds参数进行分页,这种方式侵入性最小,我们可以看到,通过RowBounds方式调用只是使用了这个参数,并没有增加其他任何内容。
分页插件检测到使用了RowBounds参数时,就会对该查询进行物理分页。
关于这种方式的调用,有两个特殊的参数是针对 RowBounds
的,你可以参看上面的 场景一 和 场景二
注:不只有命名空间方式可以用RowBounds,使用接口的时候也可以增加RowBounds参数,例如:
//这种情况下也会进行物理分页查询
List<User> selectAll(RowBounds rowBounds);
注意: 由于默认情况下的 RowBounds
无法获取查询总数,分页插件提供了一个继承自 RowBounds
的 PageRowBounds
,这个对象中增加了 total
属性,执行分页查询后,可以从该属性得到查询总数。
3.2 方法2 PageHelper.startPage 静态方法调用
除了 PageHelper.startPage
方法外,还提供了类似用法的 PageHelper.offsetPage
方法。
在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage
静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。
例1:
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
//紧跟着的第一个select方法会被分页
List<User> list = userMapper.selectIf(1);
assertEquals(2, list.get(0).getId());
assertEquals(10, list.size());
//分页时,实际返回的结果list类型是Page<E>,如果想取出分页信息,需要强制转换为Page<E>
assertEquals(182, ((Page) list).getTotal());
例2:
1 //request: url?pageNum=1&pageSize=10
2 //支持 ServletRequest,Map,POJO 对象,需要配合 params 参数
3 PageHelper.startPage(request);
4 //紧跟着的第一个select方法会被分页
5 List<User> list = userMapper.selectIf(1);
6
7 //后面的不会被分页,除非再次调用PageHelper.startPage
8 List<User> list2 = userMapper.selectIf(null);
9 //list1
10 assertEquals(2, list.get(0).getId());
11 assertEquals(10, list.size());
12 //分页时,实际返回的结果list类型是Page<E>,如果想取出分页信息,需要强制转换为Page<E>,
13 //或者使用PageInfo类(下面的例子有介绍)
14 assertEquals(182, ((Page) list).getTotal());
15 //list2
16 assertEquals(1, list2.get(0).getId());
17 assertEquals(182, list2.size());
例3:
1 //获取第1页,10条内容,默认查询总数count
2 PageHelper.startPage(1, 10);
3 List<User> list = userMapper.selectAll();
4 //用PageInfo对结果进行包装
5 PageInfo page = new PageInfo(list);
6 //测试PageInfo全部属性
7 //PageInfo包含了非常全面的分页属性
8 assertEquals(1, page.getPageNum());
9 assertEquals(10, page.getPageSize());
10 assertEquals(1, page.getStartRow());
11 assertEquals(10, page.getEndRow());
12 assertEquals(183, page.getTotal());
13 assertEquals(19, page.getPages());
14 assertEquals(1, page.getFirstPage());
15 assertEquals(8, page.getLastPage());
16 assertEquals(true, page.isFirstPage());
17 assertEquals(false, page.isLastPage());
18 assertEquals(false, page.isHasPreviousPage());
19 assertEquals(true, page.isHasNextPage());
3.3 方法3
想要使用参数方式,需要配置 supportMethodsArguments
参数为 true
,同时要配置 params
参数。 例如下面的配置:
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="supportMethodsArguments" value="true"/>
<property name="params" value="pageNum=pageNumKey;pageSize=pageSizeKey;"/>
</plugin>
</plugins>
在 MyBatis 方法中:
List<User> selectByPageNumSize(
@Param("user") User user,
@Param("pageNumKey") int pageNum,
@Param("pageSizeKey") int pageSize);
当调用这个方法时,由于同时发现了 pageNumKey
和 pageSizeKey
参数,这个方法就会被分页。params 提供的几个参数都可以这样使用。
除了上面这种方式外,如果 User 对象中包含这两个参数值,也可以有下面的方法:
List<User> selectByPageNumSize(User user);
当从 User 中同时发现了 pageNumKey
和 pageSizeKey
参数,这个方法就会被分页。
注意:pageNum
和 pageSize
两个属性同时存在才会触发分页操作,在这个前提下,其他的分页参数才会生效。
4. 安全性分析
PageHelper
方法使用了静态的 ThreadLocal
参数,分页参数和线程是绑定的。
只要你可以保证在 PageHelper
方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper
在 finally
代码段中自动清除了 ThreadLocal
存储的对象。
如果代码在进入 Executor
前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement
时), 这种情况由于线程不可用,也不会导致 ThreadLocal
参数被错误的使用。
但是如果你写出下面这样的代码,就是不安全的用法:
PageHelper.startPage(1, 10);
List<User> list;
if(param1 != null){
list = userMapper.selectIf(param1);
} else {
list = new ArrayList<User>();
}
这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
上面这个代码,应该写成下面这个样子:
List<User> list;
if(param1 != null){
PageHelper.startPage(1, 10);
list = userMapper.selectIf(param1);
} else {
list = new ArrayList<User>();
}
这种写法就能保证安全。
如果你对此不放心,你可以手动清理 ThreadLocal
存储的分页参数,可以像下面这样使用:
List<User> list;
if(param1 != null){
PageHelper.startPage(1, 10);
try{
list = userMapper.selectAll();
} finally {
PageHelper.clearPage();
}
} else {
list = new ArrayList<User>();
}
这么写很不好看,而且没有必要。