分页插件PageHelper的使用

2,035 阅读6分钟

简述

说起分页,大家都不陌生哈,几乎所有的显示列表都需要使用分页技术。话不多说,先来介绍一下什么是PageHelper,PageHelper是国内非常优秀的一款开源的mybatis分页插件,它支持基本主流与常用的数据库,

包括mysql、oracle、DB2等等,PageHelper支持任何复杂的单表、多表分页。

PageHelper是一个Mybatis的分页插件, 负责将已经写好的sql语句, 进行分页加工。

优点:无需你自己去封装以及关心sql分页等问题,使用很方便,前端取数据也很方便。

传统分页

俗话说没有对比,就不会看到分页插件的方便性,先看一下我们传统的分页过程,当然了都是基于mybatis框架。

首先我们需要自己编写一个Page的工具类,自己定义分页的一些基本信息。话不多说,上代码:

import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Page {
    //当前页数
	private int  pageNow=1;
    //每页显示的数量
	private int pageSize=2;
    //偏移量
	private int offset;
    //查询的总数量
	private int totalCount;
    //查询的总页数
	private int totalPages;
    //数据域
	private List<?> datas;
	private Map<String,String> query;
	
	public Page() {
		query=new HashMap<String, String>();
	}
    //计算偏移量
	public int getOffset() {
		return (pageNow-1)*pageSize;
	}
    //计算总页数
	public int getTotalPages() {
		boolean flag=totalCount%pageSize==0;
		int num=totalCount/pageSize;
		return flag?num:num+1;
	}
	public Map<String, String> getQuery() {
		return query;
	}

	public void setQuery(Map<String, String> query) {
		this.query = query;
	}

	public int getPageNow() {
		return pageNow;
	}
	public void setPageNow(int pageNow) {
		this.pageNow = pageNow;
	}
	public int getPageSize() {
		return pageSize;
	}
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}
	public void setOffset(int offset) {
		this.offset = offset;
	}
	public int getTotalCount() {
		return totalCount;
	}
	public void setTotalCount(int totalCount) {
		this.totalCount = totalCount;
	}
	public void setTotalPages(int totalPages) {
		this.totalPages = totalPages;
	}
	public List<?> getDatas() {
		return datas;
	}
	public void setDatas(List<?> datas) {
		this.datas = datas;
	}
}

话不多说,直接看定义了Dao接口

List<User> GetUserList(Page page);
int getTotalCount(Page page);

根据Dao接口直接编写对应的Mapper.xml:

<select id="GetUserList" parameterType="com.os.framework.rabc.commons.model.Page" resultMap="UserRoleResultMap">
    	SELECT 
    	<include refid="sys_user_columns"/>
    	FROM sys_user
    	LIMIT #{offset},#{pageSize}
</select>
<select id="getTotalCount" parameterType="com.os.framework.rabc.commons.model.Page" resultType="_int">
    	SELECT COUNT(*) FROM sys_user
</select>

接下来的步骤就不叙述了,相信大家已经都知道了,依次传递到Service层、Contoller层,之后与前端界面进行交互实现数据的分页。

接下来就让我们看一下强大的分页插件 pageHelper

pageHelper下载地址

要想明白的在mybatis中使用pageHelper这个插件,就需要我们了解mybatis中插件的运行原理,这个相信大家都已经明白,无非就是改变一下mybatis中四大核心对象的行为,采用代理模式以及责任链的方式哈。不明白的可以自行百度一下哈。有助于理解!

pageHelper使用过程

  1. 引入maven依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.11</version>
</dependency>
  1. 配置文件的配置

​ 作为mybatis的插件使用,当然我们要将其配置在configuration标签中的plugins标签中对插件进行拦截

<plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <!--属性可以自己选择 -->
            <!--方言的选择,当然了pageHelper会进行自动检测 -->
            <!--<property name="dialect" value="mysql"/>-->
            <!--默认值false,对RowRounds作为分页参数时有用 -->
            <property name="offsetAsPageNum" value="false"/>
            <!--默认值false,对RowRounds作为分页参数时有用 -->
            <property name="rowBoundsWithCount" value="false"/>
            <!--默认值false,设为true相当于没有执行分页查询,但是返回类型还是page类型 -->
            <property name="pageSizeZero" value="true"/>
            <!--分页合理化参数,默认值为 false-->
            <property name="reasonable" value="true"/>
            <!--支持通过 Mapper 接口参数来传递分页参数,默认值为false-->
            <property name="supportMethodsArguments" value="false"/>
            <!--always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
            <property name="returnPageInfo" value="none"/>
        </plugin>
 </plugins>

​ 当然了,你也可以在spring的sqlsessionfactorybean中增加一个分页拦截器属性

  1. 调用pageHelper的方法

这里我只写了一个测试类

@Test
    public void testSelectAll() {
        Page<User> page = PageHelper.startPage(1, 3);
        userMapper.selectAll();
        //使用PageHelper.startPage只是针对接下来的一条查询语句,
        //如果又查询了一次数据,则还需要使用一次PageHelper.startPage
        logger.info("获取所有User信息,获得记录数:{}", page.size());
        logger.info("获取所有User信息,获得记录:{}", page);
        //使用PageInfo封装
        PageInfo<User> info = new PageInfo<User>(page);
        logger.info("info.getPages:{}",info.getPages());
    }

当然了,在实际中我们需要在Service层中调用PageHelper的静态方法startPage(注意一定要在实际查询数据库之前调用该方法),传入需要查询的页号和每页大小,返回PageHelper插件提供的PageInfo对象。即可自动完成数据库物理分页。

pageInfo

上面我们可以看到当pageHelper调用startPage方法后一般会返回一个由PageHelper提供的PageInfo对象

我们可以直接查看一下pageInfo类的源码:

public class PageInfo<T> implements Serializable {
  private static final long serialVersionUID = 1L;
  //当前页
  private int pageNum;
  //每页的数量
  private int pageSize;
  //当前页的数量
  private int size; 
  //由于startRow和endRow不常用,这里说个具体的用法
  //可以在页面中"显示startRow到endRow 共size条数据"
  //当前页面第一个元素在数据库中的行号
  private int startRow;
  //当前页面最后一个元素在数据库中的行号
  private int endRow;
  //总记录数
  private long total;
  //总页数
  private int pages;
  //结果集
  private List<T> list;
  
  //前一页
  private int prePage;
  //下一页
  private int nextPage;
  
  //是否为第一页
  private boolean isFirstPage = false;
  //是否为最后一页
  private boolean isLastPage = false;
  //是否有前一页
  private boolean hasPreviousPage = false;
  //是否有下一页
  private boolean hasNextPage = false;
  //导航页码数
  private int navigatePages;
  //所有导航页号
  private int[] navigatepageNums;
  //导航条上的第一页
  private int navigateFirstPage;
  //导航条上的最后一页
  private int navigateLastPage;
  
  public PageInfo() {
  }
  /**
   * 包装Page对象
   *
   * @param list
   */
  public PageInfo(List<T> list) {
    this(list, 8);
  }
  
  /**
   * 包装Page对象
   *
   * @param list     page结果
   * @param navigatePages 页码数量
   */
  public PageInfo(List<T> list, int navigatePages) {
    if (list instanceof Page) {
      Page page = (Page) list;
      this.pageNum = page.getPageNum();
      this.pageSize = page.getPageSize();
  
      this.pages = page.getPages();
      this.list = page;
      this.size = page.size();
      this.total = page.getTotal();
      //由于结果是>startRow的,所以实际的需要+1
      if (this.size == 0) {
        this.startRow = 0;
        this.endRow = 0;
      } else {
        this.startRow = page.getStartRow() + 1;
        //计算实际的endRow(最后一页的时候特殊)
        this.endRow = this.startRow - 1 + this.size;
      }
    } else if (list instanceof Collection) {
      this.pageNum = 1;
      this.pageSize = list.size();
  
      this.pages = this.pageSize > 0 ? 1 : 0;
      this.list = list;
      this.size = list.size();
      this.total = list.size();
      this.startRow = 0;
      this.endRow = list.size() > 0 ? list.size() - 1 : 0;
    }
    if (list instanceof Collection) {
      this.navigatePages = navigatePages;
      //计算导航页
      calcNavigatepageNums();
      //计算前后页,第一页,最后一页
      calcPage();
      //判断页面边界
      judgePageBoudary();
    }
  }
.......
}

如上面所示,基本就是分页的逻辑,如果要看更多的源码,直接github上面就可以!

总结

总结一下哈,反正就是比传统我们自己手写的分页方便多了,我们也没有必要在编写sql语句的时候加上limit关键字了哈,同时你也会发现前端取数据也很方便。