分页功能PageHelper
2022-07-25 11:16 宋海宾 阅读(172) 评论(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>(); }
这么写很不好看,而且没有必要。