用于 Mybatis 查询时快速使用分页功能,官方详细文档地址:PageHelper 插件
此处只记录简单使用的配置
使用配置 导入 maven 依赖 1 2 3 4 5 <dependency > <groupId > com.GitHub.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > RELEASE</version > </dependency >
在 MyBatis 配置文件中配置拦截器插件 plugins 标签在配置文件中的位置必须符合要求,否则会报错,顺序如下
<properties>
<settings>
<typeAliases>
<typeHandlers>
<objectFactory>
<objectWrapperFactory>
<plugins>
<environments>
<databaseIdProvider>
<mappers>
将如下插件标签加入配置文件
1 2 3 4 5 6 7 <plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > <property name ="param1" value ="value1" /> </plugin > </plugins >
插件的简单使用 通过MyBatis PageHelper
进行分页查询实际上非常简单,只需在 service(或 mapper)方法执行查询前,调用一次 PageHelper.startPage(pageNum,pageSize)
来设置分页查询参数即可,其中pageNum
为记录页数,pageSize
为单页记录数量。此时 service(或 mapper)方法的查询结果就是分页后的结果了。
如果期望获得相关的分页信息,还可以将查询结果封装到PageInfo 对象中,以获得总页数、总记录数、当前页数等相关分页信息
例子
该实例用于分页查询一个书籍列表中的书籍信息,创建 Junit 测试来测试分页结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class BookQuery { private static ApplicationContext ctx = new ClassPathXmlApplicationContext ("applicationContext.xml" ); private static BookMapper bookMapper = (BookMapper) ctx.getBean("bookMapper" ); public List<Book> findPages (int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); return bookMapper.selectAll(); } } @Test public void TestPageHelper () { BookQuery bookQuery=new BookQuery (); List<Book> pages =bookQuery.findPages(1 , 2 ); for (Book book:pages){ System.out.println("book = " + book); } }
对于数据库,我们定义一个随机数据集
ISBN 书名 定价 出版日期 9780321928429 C Primer Plus, 6th Edition 65 2012-12-20 9787111544937 深入理解计算机系统 110 2016-08-18 9787115317957 Machine Learning in Action 78 2013-06-18 9787115478771 Deep learning with TensorFlow 64 2018-12-26 9787115490841 动手学深度学习 58 2019-06-26
执行 JUnit 单元测试,输出结果如下
1 2 3 4 book = Book{ISBN=9780321928429 书名=C Primer Plus, 6th Edition 价格=65.0 出版日期=2012-12-20} book = Book{ISBN=9787111544937 书名=深入理解计算机系统 价格=110.0 出版日期=2016-08-18} book = Book{ISBN=9787115317957 书名=Machine Learning in Action 价格=78.0 出版日期=2013-06-18} book = Book{ISBN=9787115478771 书名=Deep learning with TensorFlow 价格=64.0 出版日期=2018-12-26}
修改查询时传入的参数,List<Book> pages =bookQuery.findPages(1, 2)
,即查询第一页,并规定页面大小为 2,输出结果如下
1 2 book = Book{ISBN=9780321928429 书名=C Primer Plus, 6th Edition 价格=65.0 出版日期=2012-12-20} book = Book{ISBN=9787111544937 书名=深入理解计算机系统 价格=110.0 出版日期=2016-08-18}
PageInfo 类 对于 PageHelper,它的方法使用了静态的ThreadLocal
参数,分页参数和线程是绑定的
当我们对一个线程执行分页查询时,我们有的时候想获取它的分页信息,PageHelper 提供了一个更加强大的分页类PageInfo
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 public class BookQuery { private static ApplicationContext ctx = new ClassPathXmlApplicationContext ("applicationContext.xml" ); private static BookMapper bookMapper = (BookMapper) ctx.getBean("bookMapper" ); public PageInfo<Book> findPageInfo (int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); return new PageInfo <>(bookMapper.selectAll()); } } @Test public void TestPageInfo () { BookQuery bookQuery = new BookQuery (); PageInfo<Book> page = bookQuery.findPageInfo(2 , 2 ); System.out.println("当前页号 = " + page.getPageNum()); System.out.println("页面大小 = " + page.getPageSize()); System.out.println("当前页面开始行号 = " + page.getStartRow()); System.out.println("当前页面结束行号 = " + page.getEndRow()); System.out.println("总记录数量 = " + page.getTotal()); System.out.println("总页号 = " + page.getPages()); System.out.println("获取第一页 = " + page.getNavigateFirstPage()); System.out.println("获取最后一页 = " + page.getNavigateLastPage()); System.out.println("当前是否为第一页? : " + page.isIsFirstPage()); System.out.println("当前是否为最后一页? : " + page.isIsLastPage()); System.out.println("是否有上一页? : " + page.isHasPreviousPage()); System.out.println("是否有下一页? : " + page.isHasNextPage()); }
对上述代码的操作,依然使用测试数据集,结果输出如下
1 2 3 4 5 6 7 8 9 10 11 12 当前页号 = 2 页面大小 = 2 当前页面开始行号 = 3 当前页面结束行号 = 4 总记录数量 = 5 总页号 = 3 获取第一页 = 1 获取最后一页 = 3 当前是否为第一页? : false 当前是否为最后一页? : false 是否有上一页? : true 是否有下一页? : true
保证方法的安全性 PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
只要可以保证在PageHelper
方法调用后紧跟MyBatis
查询方法,这就是安全的 。因为PageHelper
在finally
代码段中自动清除了ThreadLocal
存储的对象
如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到MappedStatement
时),这种情况由于线程不可用,也不会导致 ThreadLocal
参数被错误的使用。
但是如果代码形式如下,就是不安全的用法
1 2 3 4 5 6 7 PageHelper.startPage(1 , 10 ); List<Book> list; if (param1 != null ){ list = BookMapper.selectIf(param1); } else { list = new ArrayList <Book>(); }
这种情况下由于param1
存在null
的情况,若 Mybatis 的查询语句没有被查询,这就会导致PageHelper
生产了一个分页参数,但是没有被消费 ,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
如果上述样例改写成如下的形式,就能保证安全
1 2 3 4 5 6 7 List<Book> list; if (param1 != null ){ PageHelper.startPage(1 , 10 ); list = BookMapper.selectIf(param1); } else { list = new ArrayList <Book>(); }
这种写法就能保证安全。
如果你对此不放心,你可以手动清理ThreadLocal
存储的分页参数,可以像下面这样使用:
1 2 3 4 5 6 7 8 9 10 11 List<Book> list; if (param1 != null ){ PageHelper.startPage(1 , 10 ); try { list = BookMapper.selectIf(param1); } finally { PageHelper.clearPage(); } } else { list = new ArrayList <Book>(); }
不推荐使用该写法 ,推荐将分页方法与 Mybatis 绑定执行,这样便能保证用法安全