将满足条件的数据, 按一定数量分成多组, 每次只查询其中的一组
每组的数量 页大小
pageSize
每组的编号 页号
pageNum
实现分页的SQL 不同的DBMS各不相同
-- MySQL:
SELECT * FROM 表名称 LIMIT 页大小*(页号-1) , 页大小
-- Oracle:
SELECT *
FROM
(
SELECT ROWNUM r_u,t1.*
FROM 表名称 t1
WHERE ROWNUM <= 页大小*(页号-1) + 页大小
) t2
WHERE t2.r_u > 页大小*(页号-1)
-- SQL Server:
SELECT TOP 页大小 *
FROM 表名称
WHERE id NOT IN
(
SELECT TOP 页大小*(页号-1) id FROM 表名称 ORDER BY id
)
ORDER BY id
可以发现虽然不同DBMS的SQL完成不同, 但包含 :
从哪条开始 firstRow = 页大小*(页号-1) = pageSize * ( pageNum -1 )
每页最多显示多少条 maxRows = 页大小 = pageSize
页面展示数据信息及操作
数据包含:
select count(*) rowCount from 表名称
pageCount = rowCount%pageSize==0?rowCount/pageSize:(rowCount/pageSize+1);
操作包含:
上一页
pageNum = pageNum - 1>0?pageNum - 1:1;
下一页
pageNum = pageNum +1>pageCount>?pageCount:( pageNum +1);
第一页(首页) pageNum=1;
最后一页(尾页) pageNum=pageCount;
Pagehelper是基于 Mybatis 框架实现分页组件
Pagehelper与springboot整合地址 : https://github.com/pagehelper/pagehelper-spring-boot
在 文档中可以找到
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.4.1version>
dependency>
如果与 Mybatis plus 整合时
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>1.4.1version>
<exclusions>
<exclusion>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
exclusion>
<exclusion>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
exclusion>
exclusions>
dependency>
这些也可以直接使用默认, 就是不用配置
#标识是数据库方言
pagehelper.helperDialect=mysql
#启用合理化,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页
pagehelper.reasonable=true
#为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
pagehelper.params=count=countSql
#支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页
pagehelper.supportMethodsArguments=true
#如果 pageSize=0 就会查询出全部的结果(相当于没有执行分页查询)
pagehelper.pageSizeZero=true
封装 分页/条件查询
import lombok.Data;
@Data
public class TabStudentPage {
// 页号
private Integer pageNum = 1;
// 页大小
private Integer pageSize = 10;
//stu_name 模糊
private String stuName;
//stu_sn 精确
private String stuSn;
}
在controller类的方法
@RequestMapping("/tabStudent/query")
public String query(TabStudentPage page, ModelMap modelMap){
System.out.println("page = " + page);
modelMap.put("page", page);
PageHelper.startPage(page.getPageNum(), page.getPageSize());
List<TabStudent> list = tabStudentService.listByPage(page);
System.out.println("list = " + list);
// 存储到 request 作用域
//modelMap.put("list", list);
modelMap.addAttribute("pageInfo", new PageInfo<TabStudent>(list));
// 内部转到 列表页 list
return "tabstudent/list_student";
}
注意点:
PageHelper.startPage(page.getPageNum(), page.getPageSize());
与 List
这两句之间不能出现异常
将结果 list 封装成 PageInfo 类对象, 包含了分页所需要的信息
PageInfo{pageNum=1, pageSize=10, size=2, startRow=1, endRow=2, total=2, pages=1, list=Page{count=true, pageNum=1, pageSize=10, startRow=0, endRow=10, total=2, pages=1, reasonable=true, pageSizeZero=true}[SysUser(userId=105, userName=zhangfei, userPass=$2a$10$aUdLHKPBzRotktn7f9AR7eDm9Uvmq0ADgShMohFU9CAhvOHCHrOHq, fullName=张飞, userState=1, userCreatetime=2022-06-29T00:35:35, userCount=2, userLastdate=2022-06-29T00:37:07, userPic=null, remark=null, userSex=1, userTel=13109876543), SysUser(userId=16, userName=admin, userPass=$2a$10$qT0ko9eQEV5ocymMAoh0zONAGqqVRiKb4CLxtlGRNAvT9.sjjqPsC, fullName=管理员, userState=1, userCreatetime=2022-05-30T21:00:42, userCount=662, userLastdate=2022-10-06T15:08:51, userPic=null, remark=管理员, userSex=1, userTel=13900009999)], prePage=0, nextPage=0, isFirstPage=true, isLastPage=true, hasPreviousPage=false, hasNextPage=false, navigatePages=8, navigateFirstPage=1, navigateLastPage=1, navigatepageNums=[1]}
private int pageNum;//当前页码
private int pageSize;//设置每页多少条数据
private int size;//当前页有多少条数据
private int startRow;//当前页码第一条数据的
private int endRow;//当前页码的开始条
private int pages;//当前页码结束条
private int prePage;//上一页(页面链接使用)
private int nextPage;//下一页(页面链接使用)
private boolean isFirstPage;//是否为第一页
private boolean isLastPage;//是否为最后一页
private boolean hasPreviousPage;//是否有前一页
private boolean hasNextPage;//是否有下一页
private int navigatePages;//导航页码数(就是总共有多少页)
private int[] navigatepageNums;//导航页码数(就是总共有多少页),可以用来遍历
private int navigateFirstPage;//首页号
private int navigateLastPage;//尾页号
页面自定义组件 templates/include/page.html
DOCTYPE html>
<html lang="zh-cn" xmlns:th="http://www.thymeleaf.org">
<div th:fragment="page(uri,page,formName)">
<style>
.zdiv{
display: inline-block;
margin-left: 50%;
transform: translate(-50%);
font-size: 12px;
}
.zpage{
list-style-type: disc;
display: -ms-flexbox;
display: flex;
padding-left: 0;
list-style: none;
border-radius: .25rem;
}
.zpage-link:hover {
z-index: 2;
color: #0056b3;
text-decoration: none;
background-color: #e9ecef;
border-color: #dee2e6;
}
.zpage-link {
position: relative;
/* display: block; */
padding: .5rem .75rem;
margin-left: -1px;
line-height: 1.25;
color: #007bff;
background-color: #fff;
border: 1px solid #dee2e6;
text-decoration:none;
}
.zpage-item:first-child .zpage-link {
margin-left: 0;
border-top-left-radius: .25rem;
border-bottom-left-radius: .25rem;
}
.zpage-item:last-child .zpage-link {
border-top-right-radius: .25rem;
border-bottom-right-radius: .25rem;
}
.zpage-item.active .zpage-link {
z-index: 3;
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.zpage-item.disabled .zpage-link {
color: #6c757d;
pointer-events: none;
cursor: auto;
background-color: #fff;
border-color: #dee2e6;
}
.zgselect{
margin: -9px 0px 0px 0px;
padding: .44rem .25rem;
border-left: 0;
border-right: 0;
}
.zginput{
margin: -9px 0px 0px 0px;
padding: .54rem .25rem;
text-align: center;
width: 50px;
}
.zgbtn{
margin-top: -.75rem;
cursor: pointer;
}
style>
<div class="zdiv">
<ul class="zpage">
<li th:if="${page.isFirstPage}" class="zpage-item disabled"><a class="zpage-link" href="javascript:pagego('1')">首页a>li>
<li th:if="${!page.isFirstPage}" class="zpage-item"><a class="zpage-link" href="javascript:pagego('1')">首页a>li>
<li th:if="${page.hasPreviousPage}" class="zpage-item"><a class="zpage-link" th:href="|javascript:pagego(${page.prePage})|">上一页a>li>
<li th:if="${!page.hasPreviousPage}" class="zpage-item disabled"><a class="zpage-link" th:href="|javascript:pagego(${page.prePage})|">上一页a>li>
<li th:if="${page.hasNextPage}" class="zpage-item"><a class="zpage-link" th:href="|javascript:pagego(${page.nextPage})|">下一页a>li>
<li th:if="${!page.hasNextPage}" class="zpage-item disabled"><a class="zpage-link" th:href="|javascript:pagego(${page.nextPage})|">下一页a>li>
<li th:if="${page.isLastPage}" class="zpage-item disabled"><a class="zpage-link" th:href="|javascript:pagego(${page.pages})|">尾页a>li>
<li th:if="${!page.isLastPage}" class="zpage-item"><a class="zpage-link" th:href="|javascript:pagego(${page.pages})|">尾页a>li>
<li class="zpage-item"><span class="zpage-link">第<span th:text="${page.pageNum}">span>/<span th:text="${page.pages}">span>页span>li>
<li class="zpage-item"><span class="zpage-link">共<span th:text="${page.total}">span>条span>li>
<li class="zpage-item">
<select class="zgselect zpage-link" onchange="setPageSize(this.value)">
<option value="5" th:selected="${5 eq page.pageSize ? true : false}">5option>
<option value="10" th:selected="${10 eq page.pageSize ? true : false}">10option>
<option value="20" th:selected="${20 eq page.pageSize ? true : false}">20option>
<option value="30" th:selected="${30 eq page.pageSize ? true : false}">30option>
<option value="50" th:selected="${50 eq page.pageSize ? true : false}">50option>
select>
li>
<li class="zpage-item">
<input class="zginput zpage-link" type="text" th:value="${page.pageNum}">
li>
<li class="zpage-item">
<a class="zgbtn zpage-link" href="javascript:pagego(document.getElementsByClassName('zginput')[0].value)">跳转a>
li>
ul>
div>
<script type="text/javascript" th:inline="javascript">
function pagego(shu, hang){
var formName = [[${formName}]];
var url = [[${uri}]];
url = appendPrifix(url);
var fh = urlForParameter(url);
var pageSize = hang;
if(pageSize == null){
pageSize = [[${page.pageSize}]];
}
if (formName != '') {
createHiddenInput("pageNum",shu,document.getElementsByName(formName)[0])
createHiddenInput("pageSize",pageSize,document.getElementsByName(formName)[0])
document.getElementsByName(formName)[0].action = url;
document.getElementsByName(formName)[0].submit();
}else{
var forms = document.getElementsByTagName("form");
if (forms != null && forms.length > 0) {
createHiddenInput("pageNum",shu,forms[0])
createHiddenInput("pageSize",pageSize,forms[0])
forms[0].action = url;
forms[0].submit();
} else {
location.href = url+fh+"pageNum="+shu+"&pageSize="+pageSize;
}
}
}
function appendPrifix(url){
return url.substring(0,1)==="/" ? url : "/"+url;
}
function urlForParameter(url){
return url.indexOf("?") > -1 ? '&' : '?';
}
function setPageSize(pageSize){
pagego([[${page.pageNum}]], pageSize);
}
//创建隐藏域
function createHiddenInput(name,value,form){
var hiddeninput = document.createElement("input");
hiddeninput.type = "hidden";
hiddeninput.name = name;
hiddeninput.value = value;
form.appendChild(hiddeninput);
}
script>
div>
html>
在 列表页面 上使用分页组件
<div th:replace="include/page::page(uri='/tabStudent/query', page=${pageInfo}, formName='searchForm')">div>
在 列表的表格中要使用
<tr th:each="row,i:${pageInfo.list}" >
<td class="a-center ">
<input type="checkbox" class="flat table_records" name="ids" th:value="${row.stuId}" >
td>
<td th:text="${pageInfo.startRow + i.index}" >121td>
<td th:text="${row.stuName}" >Maytd>
...
tr>
条件查询与分页要结合到一起
因为 先 条件查询 , 再点击 分页时, 条件查询的信息不能丢失
条件查询通常是使用表单 form
而 分页通常只传递 页号/页大小 两个信息
所以在 页面自定义组件中 要将两者 整合在一起
查询有 200W 以上记录的表时
@RequestMapping("/student/queryAll")
public List<Student> queryAll(){
// 调用 全查方法
List<Student> list = studentService.list();
System.out.println("list = " + list.size());
// 内部转到 列表页 list
return list.subList(1, 10);
}
p6spy 监测信息
SQL耗时【4749毫秒】
最终执行SQL【SELECT student_id,student_name,student_enrollmenttime,student_tel,education_id,student_weight,student_bloodtype,student_sex FROM student】
list = 2000020
@RequestMapping("/student/query")
public PageInfo query(StudentPage page){
System.out.println("page = " + page);
// 调用 全查方法
PageHelper.startPage(page.getPageNum(),page.getPageSize());
List<Student> list = studentService.listForPage(page);
System.out.println("list = " + list);
// 内部转到 列表页 list
return new PageInfo<Student>(list);
}
p6spy 监测信息
page = StudentPage(pageNum=1, pageSize=10, name=null)
SQL耗时【360毫秒】
最终执行SQL【SELECT count(0) FROM student】
SQL耗时【0毫秒】
最终执行SQL【select student_id,student_name,student_enrollmenttime,
student_tel,education_id,student_weight,
student_bloodtype,student_sex
from student LIMIT 10 】
将 PageHelper.startPage(页号, 页大小 , 是否求总记录数);
@RequestMapping("/student/queryNotCount")
public PageInfo queryNotCount(StudentPage page){
System.out.println("page = " + page);
// 调用 全查方法
PageHelper.startPage(page.getPageNum(),page.getPageSize(), false);
List<Student> list = studentService.listForPage(page);
System.out.println("list = " + list);
// 内部转到 列表页 list
return new PageInfo<Student>(list);
}
p6spy 监测信息
page = StudentPage(pageNum=1, pageSize=10, name=null)
SQL耗时【1毫秒】
最终执行SQL【select student_id,student_name,student_enrollmenttime,
student_tel,education_id,student_weight,
student_bloodtype,student_sex
from student LIMIT 10 】
可以看到明显的时间差
当数据量非常大时,为了保证效率, 一定要用分页, 而且可以不查总记录数