简述
说起分页,大家都不陌生哈,几乎所有的显示列表都需要使用分页技术。话不多说,先来介绍一下什么是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中四大核心对象的行为,采用代理模式以及责任链的方式哈。不明白的可以自行百度一下哈。有助于理解!
- github的下载地址 github.com/pagehelper/…
- maven的引入地址 mvnrepository.com/artifact/co…
pageHelper使用过程
- 引入maven依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
- 配置文件的配置
作为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的sqlsessionfactory的bean中增加一个分页拦截器属性
- 调用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关键字了哈,同时你也会发现前端取数据也很方便。