在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。
前端分页
一次性请求数据表格中的所有记录(ajax),然后在前端缓存并且计算count和分页逻辑,一般前端组件(例如dataTable)会提供分页动作。
特点是:简单,很适合小规模的web平台;当数据量大的时候会产生性能问题,在查询和网络传输的时间会很长。
后端分页
在ajax请求中指定页码(pageNum)和每页的大小(pageSize),后端查询出当页的数据返回,前端只负责渲染。
特点是:复杂一些;性能瓶颈在MySQL的查询性能,这个当然可以调优解决。一般来说,web开发使用的是这种方式。
我们说的也是后端分页。
Mybatis分页插件PageHelper,国内牛人的一个开源项目,有兴趣的可以去看源码,都有中文注释
github上仓库地址为:GitHub - pagehelper/Mybatis-PageHelper: Mybatis通用分页插件
oschub的项目地址:Mybatis_PageHelper: Mybatis分页插件
它支持基本主流与常用的数据库,这可以在它的文档上看到。
1、POM依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.4</version>
</dependency>
2、Mybatis对PageHelper的配置
打开Mybatis配置文件,一般在Resource路径下。我这里叫mybatis-config.xml。
一些配置参数的说明可以参考文档: 【分页插件参数介绍】
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--PageHelper分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--PageHelper插件4.0.0以后的版本支持自动识别使用的数据库-->
</plugin>
</plugins>
</configuration>
4、Service接口
List<Order> getList(Order order);
5、Service接口实现类
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<Order> getList(Order order) {
List<Order> list = userDao.FindUserByInfo(order);
return list;
}
}
优化后使用
public PageInfo<ResponseEntityDto> page(order param) {
return PageHelper.startPage(param.getPageNum(), param.getPageSize())
.doSelectPageInfo(() -> list(param))
}
public List<ResponseEntityDto> list(order param) {
return userDao.getList(param);
}
6、Controller代码
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* order 实体类
* page 当前页
* pageSize 当前页的数量(我这里是10条)
*/
@RequestMapping("/user.do")
public String userList( Model model, Order order,
@RequestParam(required = true, defaultValue = "1") Integer page,
@RequestParam(required = false, defaultValue = "10") Integer pageSize) {
//引入分页查询,使用PageHelper分页功能,在查询之前传入当前页,然后多少记录
PageHelper.startPage(page, pageSize);
List<Order> userList = userService.getList(order);
//使用PageInfo包装查询结果,只需要将pageInfo交给页面就可以
PageInfo<Order> page = new PageInfo<Order>(userList);
/*** 分页配置信息返回*/
page.setLastPage((int) page.getTotal() / pageSize + 1);
page.setFirstPage(1);
model.addAttribute("page", page);
return "Pagination";
}
}
@PostMapping("/api/v1/task/list")
public PageInfo<order> taskList(@RequestBody PagedRequest<order> pageParam) {
PageInfo<order> page = taskService.getList(pageParam);
return page;
}
以上使用说明:
在查询调用方法前声明分页信息(当前页,每页记录数)
在查询调用方法对查询结果进行包装成PageInfo对象,也可以是定连续显示的页数,这也是常用功能。
注意:都是与查询方法紧挨着的,不要中间夹杂其它无关语句
之后把信息放在ModelAndView中就可以在前台访问了。
6、 PageInfo类说明
类源码(更多源码去github上查看即可):
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() {
}
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;
//计算导航页
this.calcNavigatepageNums();
//计算前后页,第一页,最后一页
this.calcPage();
//判断页面边界
this.judgePageBoudary();
}
}
}
7、前端页面
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<base href="<%=basePath%>">
<title>分页</title>
<script src="js/jquery-3.1.1.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
</head>
<body>
<div class="row" id="deviceRecordList">
<div class="col-lg-12" style="height: 15px;top:0px" id="paging">
<div class="table-responsive">
<table class="table table-striped table-bordered bootstrap-datatable datatable">
<thead>
<tr>
<th>序号</th>
<th>科目</th>
<th>收费类型</th>
<th>单价</th>
<th>数量</th>
<th>总金额</th>
<th>开始时间</th>
<th>结束时间</th>
<th>结束类型</th>
</tr>
</thead>
<tbody>
<c:forEach items="${userList}" var="c" varStatus="status">
<tr>
<td>${status.index+1 }</td>
<td>${c.id}</td>
<td>${c.order_date}</td>
<td>
<span class="label label-warning">${c.member_label}</span>
</td>
<td>${c.codeno}</td>
<td>${c.remark}</td>
<td>${c.order_state}</td>
<td>${c.product_money}</td>
<td>${c.send_time }</td>
</tr>
</c:forEach>
</tbody>
</table>
<!-- 分页 -->
<div style="text-align:center;" id="deviceId">
<ul class="pagination">
<c:if test="${!page.isFirstPage}">
<li><a href="user.do?page=${page.firstPage}">首页</a></li>
<li><a href="user.do?page=${page.prePage}">上一页</a></li>
</c:if>
<c:forEach items="${page.navigatepageNums}" var="navigatepageNum">
<c:if test="${navigatepageNum==page.pageNum}">
<li class="active"><a href="user.do?page=${navigatepageNum}">${navigatepageNum}</a></li>
</c:if>
<c:if test="${navigatepageNum!=page.pageNum}">
<li><a href="user.do?page=${navigatepageNum}">${navigatepageNum}</a></li>
</c:if>
</c:forEach>
<c:if test="${!page.isLastPage}">
<li><a href="user.do?page=${page.nextPage}">下一页</a></li>
<li><a href="user.do?page=${page.lastPage}">尾页</a></li>
</c:if>
<li><a href="javascript:void(0);">共${page.total}条记录,</a></li>
<%--共<i class="blue">${page.total}</i>条记录,当前显示第 <i--%>
<%--class="blue">${page.pageNum}/${page.pages}</i> 页--%>
</ul>
</div>
</div>
</div>
</div>
</body>
</html>
8、分页效果如下