目录
本文环境:Spring boot 2.3.5 + Java jdk 8 + Mysql + Log4j2 + MyBatis 3.5.7 + PageHelper 5.2.1
PageHelper 分页插件概述
PageHelper 官网:MyBatis 分页插件 PageHelper
PageHelper GitHub:GitHub - pagehelper/Mybatis-PageHelper: Mybatis通用分页插件
PageHelper 集成 Spring Boot:GitHub - pagehelper/pagehelper-spring-boot: pagehelper-spring-boot
1、PageHelper 分页插件支持任何复杂的单表、多表分页。
2、物理分页:支持常见的 12 种数据库:Oracle, MySql, MariaDB, SQLite, DB2, PostgreSQL, SqlServer 等。
3、多种分页方式:支持常见的RowBounds(PageRowBounds),PageHelper.startPage 方法调用,Mapper 接口参数调用,QueryInterceptor 拦截器方式。
4、Spring boot 项目推荐直接引用 pagehelper-spring-boot-starter 启动器:
<!--mybatis 分页插件 pagehelper 内部依赖了 mybatis-spring-boot-starter 启动器 以及 com.github.pagehelper 插件 -->
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
5、pagehelper-spring-boot-starter 启动器的自动配置类与配置属性如下,默认情况下,导入依赖后即可进行分页,如果需要自定义配置,则可以从 PageHelperProperties 中查看配置项:
com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration
com.github.pagehelper.autoconfigure.PageHelperProperties
6、application.yml 全局配置文件中 mybatis 分页插件 pageHelper 常用配置:
#mybatis 分页插件 pageHelper 配置
pagehelper:
#支持通过 Mapper 接口参数来传递分页参数,为 true 时,分页插件会从查询方法的参数值中查找 pageNum、pageSize 参数的值进行自动分页。默认值 false.
supportMethodsArguments: true
#分页合理化参数,为 true 时,pageNum<=0 会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
reasonable: true
#设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0, 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型),默认值为 false,
pageSizeZero: true
PageHelper 方式分页
1、其实分页的关键是获取符合查询 sql 条件的总条数,只要有了它,再根据传入的参数 pageNum,pageSize 就能计算出任何想要的数据,比如总页数,上一页,下一页等等。
2、比如是在代码里面写 sql 时,则只需要在目标 sql 外面再包装一层,再查询一下 count(*) 即可获取到本 sql 的总条数,而 mybatis 的 sql 因为是在配置文件中或者在注解上,比较特殊,所以无法用常规手段获取。
3、一个 Controller 中总有几个查询的接口,让其实现分页非常简单,只需要在查询前开启分页:PageHelper.startPage,然后再返回结果后,包装 Page 对象 new PageInfo(List<T> list) 即可。
4、只有紧跟在 PageHelper.startPage、PageHelper.offsetPage 方法后的第一个 Mybatis 的查询(Select)方法会被分页,除非再次调用 PageHelper.startPage 或者调用 PageHelper.offsetPage。
//控制层方法:http://localhost:8080/findByKeywordPage1?pageNum=2&pageSize=4
@GetMapping("/findByKeywordPage1")
public PageInfo findByKeywordPage1(String keyword, Integer pageNum, Integer pageSize) {
pageNum = pageNum == null || pageNum <= 0 ? 0 : pageNum;
pageSize = pageSize == null || pageSize <= 0L ? 0 : pageSize;
/**
* 1、PageHelper.startPage(int pageNum, int pageSize):开始分页,此时 XxxMapper 返回的集合是 {@link com.github.pagehelper.Page} 分页对象
* total(总记录数) 等于 ((Page)list).getTotal();
* 2、PageHelper.offsetPage:开始分页,底层与 startPage 是一样的.
* 3、如果 pageNum 或者 pageSize 为 0,则不分页,默认查询所有,此时 XxxMapper 返回的集合不再是 {@link com.github.pagehelper.Page} 分页对象
* 此时 size(当前页的数量)、total(总记录数) 都是等于普通的 list.size()
* 4、只有紧跟在 PageHelper.startPage、PageHelper.offsetPage 方法后的第一个 Mybatis 的查询(Select)方法会被分页。
*/
if (pageNum != 0 && pageSize != 0) {
//PageHelper.startPage(pageNum, pageSize);
PageHelper.offsetPage(pageNum, pageSize);
}
List<Dept> deptList = deptMapper.findByKeyword(keyword);
/**
* PageInfo(List<T> list):包装 Page 对象,默认导航页码为8
* PageInfo(List<T> list, int navigatePages):包装 Page 对象,指定导航页码数量
*/
PageInfo pageInfo = new PageInfo(deptList);
return pageInfo;
}
//DeptMapper 接口:根据关键字模糊查询,默认查询所有
List<Dept> findByKeyword(String keyword);
<!-- DeptMapper.xml 映射 -->
<!-- 根据关键字模糊查询,不为空时才拼 like 条件,否则默认查询所有.-->
<select id="findByKeyword" resultType="com.wmx.hb.pojo.Dept" parameterType="string">
select * from dept
<where>
<if test="keyword!=null">
dname like concat('%',concat(#{keyword},'%')) or loc like concat('%',concat(#{keyword},'%'))
</if>
</where>
order by deptno desc
</select>
/hb/blob/master/src/main/java/com/wmx/hb/controller/DeptController.java
/hb/blob/master/src/main/java/com/wmx/hb/mapper/DeptMapper.java
/hb/blob/master/src/main/resources/myBatis/mapper/base/DeptMapper.xml
Mapper 接口参数分页
1、只要 XxxMapper 接口的方法中含有参数 pageNum、pageSize,则会自动进行分页,不需要在 XxxMapper.xml 中进行任何处理。
2、此种方式时必须配置 pagehelper.supportMethodsArguments=true,开启支持 Mapper 方法参数分页.
//控制层方法
@PostMapping("/findByDept")
public PageInfo<Dept> findByDept(@RequestBody(required = false) Dept dept, Integer pageNum, Integer pageSize) {
pageNum = pageNum == null || pageNum <= 0L ? 1 : pageNum;
pageSize = pageSize == null || pageSize <= 0L ? 2 : pageSize;
//此时必须配置 pagehelper.supportMethodsArguments=true,开启支持 Mapper 方法参数分页.
//pageNum、pageSize 也可以直接是 dept 对象的属性,当 pageNum!= null && pageSize!= null 时,也会自动分页.
List<Dept> deptList = deptMapper.findByDept(dept, pageNum, pageSize);
PageInfo<Dept> pageInfo = new PageInfo<Dept>(deptList);
return pageInfo;
}
/**DeptMapper 接口方法
* 1、查询指定部门的信息,将实体对象中不为空的属性都作为 where 条件,默认查所有。
* 2、使用 org.apache.ibatis.annotations.Param 注解可以为 XxxMapper 接口方法指定多个参数。
* 3、多个参数会被封装成一个 map,map 的 key 是使用 @Param 注解指定的值,value 是参数值。
* 4、如果是级联对象,则 XxxMapper.xml 映射文件中也是级联取值,如 dept.deptno、dept.dname,前提是 dept 不能为 null,否则异常.
* 5、pageNum、pageSize 也可以直接是 dept 对象的属性,当 pageNum!= null && pageSize!= null 时,也会自动分页.
*
* @param dept
* @param pageNum :查询的页码,从1开始
* @param pageSize :每页显示的条数
* @return
*/
List<Dept> findByDept(@Param("dept") Dept dept, @Param("pageNum") int pageNum, @Param("pageSize") int pageSize);
<!--dept是对象,所以需要级联取值,必须不等于 null,才能级联取值-->
<!--拼接 where 条件前进行参数是否为空判断。返回值类型指定为 List 中的元素类型-->
<!--where 标签会自动删除内容开头的 and 或者 or 字符串 -->
<select id="findByDept" resultType="com.wmx.hb.pojo.Dept">
SELECT t1.deptno,t1.dname,t1.loc from dept t1
<where>
<if test="dept !=null and dept.deptno!=null">
deptno = #{dept.deptno}
</if>
<if test="dept !=null and dept.dname!=null and dept.dname !=''">
and dname like concat('%',concat(#{dept.dname},'%'))
</if>
<if test="dept !=null and dept.loc!=null and dept.loc!=''">
and loc like concat('%',concat(#{dept.loc},'%'))
</if>
</where>
</select>
ISelect 接口方式分页
1、写法和 PageHelper 方式差不多,如下所示:
//控制层方法
@GetMapping("/findByKeyword")
public PageInfo<Dept> findByKeyword(String keyword, Integer pageNum, Integer pageSize) {
pageNum = pageNum == null || pageNum <= 0 ? 0 : pageNum;
pageSize = pageSize == null || pageSize <= 0L ? 0 : pageSize;
//doSelectPage 返回的是 Page<E>对象,doSelectPageInfo 返回的是 PageInfo<E>对象.
PageInfo<Dept> pageInfo = PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(new ISelect() {
@Override
public void doSelect() {
deptMapper.findByKeyword(keyword);
}
});
return pageInfo;
}
// DeptMapper 接口方法:根据关键字模糊查询,默认查询所有
List<Dept> findByKeyword(String keyword);
<!-- DeptMapper.xml 映射文件:根据关键字模糊查询,不为空时才拼 like 条件,否则默认查询所有.-->
<select id="findByKeyword" resultType="com.wmx.hb.pojo.Dept" parameterType="string">
select * from dept
<where>
<if test="keyword!=null">
dname like concat('%',concat(#{keyword},'%')) or loc like concat('%',concat(#{keyword},'%'))
</if>
</where>
order by deptno desc
</select>
PageInfo 分页对象信息
1、PageInfo 是对 Page<E> 结果的包装,包含了整个分页的详细信息,如下所示。
2、pagehelper 的 pageinfo(分页信息) 中的许多属性实际开发中可能用不到,通常也不会直接返回 pageinfo,而是会返回自己封装的数据对象,比如会有状态码,提示信息等等,此时可以在工具类中添加方法用于从 pageinfo 中提取需要的数据。
protected long total; //总记录数 |
protected List<T> list; //结果集 |
private int pageNum; //当前页 |
private int pageSize; //每页的数量 |
private int size; //当前页的数量 |
//由于startRow和endRow不常用,这里说个具体的用法 //可以在页面中"显示startRow到endRow 共size条数据" //当前页面第一个元素在数据库中的行号 private long startRow; |
private long endRow; //当前页面最后一个元素在数据库中的行号 |
private int pages; //总页数 |
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; //导航条上的最后一页 |
{
"total": 12,
"list": [
{
"deptno": 4,
"dname": "事业部",
"loc": "沙"
},
{
"deptno": 3,
"dname": "sales",
"loc": "chicago"
},
{
"deptno": 2,
"dname": "research",
"loc": "dallas"
},
{
"deptno": 1,
"dname": "IOS开发部",
"loc": "长沙"
}
],
"pageNum": 3,
"pageSize": 4,
"size": 4,
"startRow": 9,
"endRow": 12,
"pages": 3,
"prePage": 2,
"nextPage": 0,
"isFirstPage": false,
"isLastPage": true,
"hasPreviousPage": true,
"hasNextPage": false,
"navigatePages": 8,
"navigatepageNums": [
1,
2,
3
],
"navigateFirstPage": 1,
"navigateLastPage": 3
}