代码改变世界

分页功能PageHelper

2022-07-25 11:16  宋海宾  阅读(178)  评论(0编辑  收藏  举报

1.简介

开源组件PageHelper支持分页公共,资料地址:

https://github.com/pagehelper/Mybatis-PageHelper

 

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>();
}

 

这么写很不好看,而且没有必要。