PageHelper 代码中的调用!

上一篇文件已经介绍PageHelper 集成 springboot !


分页插件支持以下几种调用方式:

//第一种,RowBounds方式的调用

List list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(0, 10));

//第二种,Mapper接口方式的调用,推荐这种使用方式。

PageHelper.startPage(1, 10);

List list = countryMapper.selectIf(1);

//第三种,Mapper接口方式的调用,推荐这种使用方式。

PageHelper.offsetPage(1, 10);

List list = countryMapper.selectIf(1);

//第四种,参数方法调用

//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数

public interface CountryMapper {

    List selectByPageNumSize(

            @Param("user") User user,

            @Param("pageNum") int pageNum,

            @Param("pageSize") int pageSize);

}

//配置supportMethodsArguments=true

//在代码中直接调用:

List list = countryMapper.selectByPageNumSize(user, 1, 10);

//第五种,参数对象

//如果 pageNum 和 pageSize 存在于 User 对象中,只要参数有值,也会被分页

//有如下 User 对象

public class User {

    //其他fields

    //下面两个参数名和 params 配置的名字一致

    private Integer pageNum;

    private Integer pageSize;

}

//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数

public interface CountryMapper {

    List selectByPageNumSize(User user);

}

//当 user 中的 pageNum!= null && pageSize!= null 时,会自动分页

List list = countryMapper.selectByPageNumSize(user);

//第六种,ISelect 接口方式

//jdk6,7用法,创建接口

Page page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {

    @Override

    public void doSelect() {

        countryMapper.selectGroupBy();

    }

});

//jdk8 lambda用法

Page page = PageHelper.startPage(1, 10).doSelectPage(()-> countryMapper.selectGroupBy());

//也可以直接返回PageInfo,注意doSelectPageInfo方法和doSelectPage

pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {

    @Override

    public void doSelect() {

        countryMapper.selectGroupBy();

    }

});

//对应的lambda用法

pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> countryMapper.selectGroupBy());

//count查询,返回一个查询语句的count数

long total = PageHelper.count(new ISelect() {

    @Override

    public void doSelect() {

        countryMapper.selectLike(country);

    }

});

//lambda

total = PageHelper.count(()->countryMapper.selectLike(country));

下面对最常用的方式进行详细介绍

1). RowBounds方式的调用

List list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));

使用这种调用方式时,你可以使用RowBounds参数进行分页,这种方式侵入性最小,我们可以看到,通过RowBounds方式调用只是使用了这个参数,并没有增加其他任何内容。

分页插件检测到使用了RowBounds参数时,就会对该查询进行物理分页

关于这种方式的调用,有两个特殊的参数是针对 RowBounds 的,你可以参看上面的 场景一 和 场景二

注:不只有命名空间方式可以用RowBounds,使用接口的时候也可以增加RowBounds参数,例如:

//这种情况下也会进行物理分页查询

List selectAll(RowBounds rowBounds); 

注意: 由于默认情况下的 RowBounds 无法获取查询总数,分页插件提供了一个继承自 RowBounds 的 PageRowBounds,这个对象中增加了 total 属性,执行分页查询后,可以从该属性得到查询总数。

2). PageHelper.startPage 静态方法调用

除了 PageHelper.startPage 方法外,还提供了类似用法的 PageHelper.offsetPage 方法。

在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage 静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。

例一:

//获取第1页,10条内容,默认查询总数count

PageHelper.startPage(1, 10);

//紧跟着的第一个select方法会被分页

List list = countryMapper.selectIf(1);

assertEquals(2, list.get(0).getId());

assertEquals(10, list.size());

//分页时,实际返回的结果list类型是Page,如果想取出分页信息,需要强制转换为Page

assertEquals(182, ((Page) list).getTotal());

例二:

//request: url?pageNum=1&pageSize=10

//支持 ServletRequest,Map,POJO 对象,需要配合 params 参数

PageHelper.startPage(request);

//紧跟着的第一个select方法会被分页

List list = countryMapper.selectIf(1);

//后面的不会被分页,除非再次调用PageHelper.startPage

List list2 = countryMapper.selectIf(null);

//list1

assertEquals(2, list.get(0).getId());

assertEquals(10, list.size());

//分页时,实际返回的结果list类型是Page,如果想取出分页信息,需要强制转换为Page

//或者使用PageInfo类(下面的例子有介绍)

assertEquals(182, ((Page) list).getTotal());

//list2

assertEquals(1, list2.get(0).getId());

assertEquals(182, list2.size());

例三,使用PageInfo的用法:

//获取第1页,10条内容,默认查询总数count

PageHelper.startPage(1, 10);

List list = countryMapper.selectAll();

//用PageInfo对结果进行包装

PageInfo page = new PageInfo(list);

//测试PageInfo全部属性

//PageInfo包含了非常全面的分页属性

assertEquals(1, page.getPageNum());

assertEquals(10, page.getPageSize());

assertEquals(1, page.getStartRow());

assertEquals(10, page.getEndRow());

assertEquals(183, page.getTotal());

assertEquals(19, page.getPages());

assertEquals(1, page.getFirstPage());

assertEquals(8, page.getLastPage());

assertEquals(true, page.isFirstPage());

assertEquals(false, page.isLastPage());

assertEquals(false, page.isHasPreviousPage());

assertEquals(true, page.isHasNextPage());

3). 使用参数方式

想要使用参数方式,需要配置 supportMethodsArguments 参数为 true,同时要配置 params 参数。 例如下面的配置:

   

   

       

       

       

在 MyBatis 方法中:

List selectByPageNumSize(

        @Param("user") User user,

        @Param("pageNumKey") int pageNum,

        @Param("pageSizeKey") int pageSize);

当调用这个方法时,由于同时发现了 pageNumKey 和 pageSizeKey 参数,这个方法就会被分页。params 提供的几个参数都可以这样使用。

除了上面这种方式外,如果 User 对象中包含这两个参数值,也可以有下面的方法:

List selectByPageNumSize(User user);

当从 User 中同时发现了 pageNumKey 和 pageSizeKey 参数,这个方法就会被分页。

注意:pageNum 和 pageSize 两个属性同时存在才会触发分页操作,在这个前提下,其他的分页参数才会生效。

3). PageHelper 安全调用

1. 使用 RowBounds 和 PageRowBounds 参数方式是极其安全的

2. 使用参数方式是极其安全的

3. 使用 ISelect 接口调用是极其安全的

ISelect 接口方式除了可以保证安全外,还特别实现了将查询转换为单纯的 count 查询方式,这个方法可以将任意的查询方法,变成一个 select count(*) 的查询方法。

4. 什么时候会导致不安全的分页?

PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。

只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。

如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。

但是如果你写出下面这样的代码,就是不安全的用法:

PageHelper.startPage(1, 10);

List list;

if(param1 != null){

    list = countryMapper.selectIf(param1);

} else {

    list = new ArrayList();

}

这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。

上面这个代码,应该写成下面这个样子:

List list;

if(param1 != null){

    PageHelper.startPage(1, 10);

    list = countryMapper.selectIf(param1);

} else {

    list = new ArrayList();

}

这种写法就能保证安全。

如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:

List list;

if(param1 != null){

    PageHelper.startPage(1, 10);

    try{

        list = countryMapper.selectAll();

    } finally {

        PageHelper.clearPage();

    }

} else {

    list = new ArrayList();

}

你可能感兴趣的:(PageHelper 代码中的调用!)