技术涵盖(JavaWeb、HTML、Ajax、JQuery、Bootstrap )
接触这一部分知识的时候,我们经常会做一些小Demo来练手,不可避免的就需要接触到一定量的数据,我们常常需要将数据从数据库中回显到页面中,但是随着数据量的增加,如果不对数据的查询或者显示进行一定的处理,那么会出现各式各样的问题,例如:
- 客户端:如果数据同时展示在一个页面中,用户体验效果比较差,操作也是极其不方便
- 服务端:一次请求,查询到所有的数据,数据传输量过大或导致超时或者响应速度变慢,对于服务器的负荷过大
分页方式
前端 JS 分页 - 不推荐
我们可以请求获取到所有数据后,使用 JavaScript 来进行数据分页显示,单纯的在数据的显示这一方面看确实美观了很多,并且这种分页方式要比后端分页简单很多
但是如果存在一定数据量的情况下,这种方式着实有一些尴尬,他并没有解决了我们服务端的任何问题,反而会让用户在等待响应数据耗时过多,体验不佳,不过它仍然是一种分页方式
在这里我们重点讲解后端分页,所以我们简单的演示一下,也把代码贴出来,由于我们 html 中使用的是 BootStrap 前端框架,所以我们借助了 bootstrap-table 这个前端分页插件
前端 JS 分页 演示代码:
![11.1-01-003](G:\公众号\markdown文件\11-分页与条件查询\分页查询\11.1-01-003.png)<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
<!-- 指定字符集 -->
<meta charset="utf-8">
<!-- 使用Edge最新的浏览器的渲染方式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。
width: 默认宽度与设备的宽度相同
initial-scale: 初始的缩放比,为1:1 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>用户信息管理系统</title>
<!-- 导入CSS的全局样式 -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!--导入表格插件样式表-->
<link href="css/bootstrap-table.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h3 style="text-align: center">用户信息列表</h3>
<!--存放工具栏-->
<div id="toolbar"></div>
<!--存放生成的表格-->
<table id="userInfo_table" class="table table-hover">
</table>
</div>
<!-- jQuery导入,建议使用1.9以上的版本 -->
<script src="js/jquery-2.1.0.min.js"></script>
<!-- 导入bootstrap的js文件 -->
<script src="js/bootstrap.min.js"></script>
<!--导入表格插件-->
<script src="js/bootstrap-table.min.js"></script>
<style type="text/css">
td, th {
text-align: center;
}
</style>
<script>
$(function () {
$("#userInfo_table").bootstrapTable({
url: 'user/userList',
toolbar: '#toolbar',
method: 'GET',
striped: true, //是否显示行间隔色
cache: false, //是否使用缓存
toolbarAlign: "right", //工具栏对齐方式
sidePagination: "client", //分页方式:client客户端分页,server服务端分页
search: true, //是否显示表格搜索,此搜索是客户端搜索,不会进服务端
uniqueId: "id",
pageNumber: 1, //初始化加载第一页
pageSize: 10, //每页的记录行数
pageList: [5, 10, 15, 20], //可供选择的每页的行数
pagination: true, // 是否分页
sortable: true, // 是否启用排序
sortOrder: "asc", //排序方式
showColumns: true, //是否显示列选择按钮
showRefresh: true, //是否显示刷新按钮
clickToSelect: true, //是否启用点击选中行
// height: 500, //行高
showToggle: true, //是否显示详细视图和列表视图的切换按钮
cardView: false, //是否显示详细视图
detailView: false, //是否显示父子表
queryParamsType: '',//设置请求参数格式
queryParams: function queryParams(params) { //设自定义查询参数
/*请求远程数据时,可以通过修改queryParams来发送其他参数。
如果queryParamsType = 'limit',params对象包含:limit,offset,search,sort,order。
否则,它包含:pageSize,pageNumber,searchText,sortName,sortOrder。
返回false停止请求。
默认: function(params) { return params }*/
return params;
},
columns: [{
title: "全选",
field: "select",
checkbox: true,
width: 20, //宽度
align: "center", //水平
valign: "middle" //垂直
}, {
field: 'uid',
title: '编号'
}, {
field: 'username',
title: '用户名'
}, {
field: 'nickname',
title: '昵称'
}, {
field: 'email',
title: '邮箱'
}, {
field: 'telephone',
title: '电话'
}, {
field: 'gender',
title: '性别'
}, {
field: 'birthday',
title: '生日'
},{
field: 'id',
title: '操作',
// width: 120,
align: 'center',
valign: 'middle',
formatter: actionFormatter
}]
})
})
//操作栏的格式化
function actionFormatter(value, row, index) {
var id = row.id;
var result = "";
result += "<button style='cursor: pointer;margin-right: 5px' class='btn btn-primary' title='修改' onclick=''>修改</button>";
result += "<button style='cursor: pointer' class='btn btn-primary' title='删除' onclick=''>删除</button>";
return result;
}
</script>
</body>
</html>
(二) 后端分页 - 推荐
后端分页与前端分页的最大不同就是,它不需要一次性向后端请求大量的数据,而是根据用户的设定,一次请求一定量的数据,然后将这些数据回显到页面上,后端分页也才是分页的正确打开方式,其避免了一次性从数据库获取很多数据,也可以美化前端展示效果,优化用户体验
后端分页的实现方式
(一) 整体分析
根据我们上面所讲的,我们需要的就是前端向后端提交请求,后端响应前端需要的数据,并且展示在前端页面中
前端页面中,我们自然需要一个分页条
我们根据需要大致改造一下,增加一个首页和末页,同时增加一个页数以及数据记录统计文字
我们数据涉及到的问题基本就是上图以及响应数据在表格中的回显
① 响应的数据,自然我们需要 将后端所传来包含 用户信息的 list 集合进行遍历回显
- 即 需要接收并处理一个
List
集合
② 总记录数,经后台在数据库查询后给出值
- 即 需要一个
int totalCount
变量 (变量名自行决定)
③ 总页码,可以根据总记录数以及每页展示的条数计算出(后面具体讲)
- 即 需要一个
int totalPage
变量
④ 当前页码,根据当前页码可以让后台知道你需要的数据是哪些
- 即 需要一个
int currentPage
变量
⑤ 每页展示的条数,这个值可以暂时写为固定的,改进时,可以交给客户端选择,并且提供给后端
- 即 需要一个
int pageSize
变量
⑥ 每次查询的起始位置,每次查询时通过 LIMIT 语句进行限制,可以结合每页显示的条数得出
- 即 需要一个
int start
变量
(二) 后端实现
(1) 分页对象
由于前端需要接收到后台传来的需要数据信息,我们可以为上面我们简单分析出所需要的东西,集合成一个分页对象,方便我们的数据传递
//为方便后期调用,加上泛型
public class PageBean<T> {
private int totalCount;//总记录数
private int totalPage;//总页数
private int currentPage;//当前页码
private int pageSize;//每页显示的条数
private List<T> list;//每页显示的数据集合
//省略对应构造,get set方法
}
(2) Servlet 代码
- 首先需要获取到前端传来的:
currentPage、pageSize
两个 String 类型的值 - 如果前端不传递,默认设置 这两个变量的值,若传递值合理,则将其类型转为 int 型(前期可以先忽略这个,或者在前端设置校验)
- 调用 service 查询 PageBean 分页对象,并接收其返回值
- 将PageBean对象序列化为 json 格式,返回
@WebServlet("/route/*")
public class RouteServlet extends BaseServlet {
/**
* 分页查询方法
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
public void routeQuery(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String currentPageStr = request.getParameter("currentPage"); //当前页码
String pageSizeStr = request.getParameter("pageSize"); // 每页显示的条数
//当前页码,如果不传递,默认为第1页
int currentPage = 0;
if (currentPageStr != null && currentPageStr.length() > 0) {
currentPage = Integer.parseInt(currentPageStr);
} else {
currentPage = 1;
}
//每页显示条数,如果不传递,默认显示8条记录
int pageSize = 0;
if (pageSizeStr != null && pageSizeStr.length() > 0) {
pageSize = Integer.parseInt(pageSizeStr);
} else {
pageSize = 8;
}
//获取条件查询参数
Map<String, String[]> condition = request.getParameterMap();
//调用service查询PageBean对象
RouteService service = new RoutrServiceImpl();
PageBean<User> userPageBean = service.pageQuery(currentPage, pageSize);
//将PageBean对象序列化为json,返回
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json;charset=utf-8");
mapper.writeValue(response.getOutputStream(), userPageBean);
}
}
说明:以上代码我抽取了Servlet,方便日后扩展方法,刚接触的朋友 直接创建一个 普通的 Servlet 直接在其中编写也是一样可以的,熟悉的朋友,请忽略我这句话
我们需要导入 jackson spring mysql druid 的相关jar包
(3) Service 代码
currentPage 和 pageSize 这两个值已经确定了,我们还需要确定的有:
总记录数 totalCount
和 总页码数 totalPage
以及需要回显到前端页面的 List
集合
总记录数我们直接通过dao层查询就可以了
总页码数我们可以通过 (总记录数 / 每页显示的条数) 确定,要注意不能整除需要多出一页
-
查询 需要在前端页面展示的数据 list 我们需要在SQL查询中 使用 LIMIT进行限制,所以我们需要提供查询 的开始点 以及每次 查多少条,这样才能准确的找到这一页 应该是哪些数据被回显到页面中,简单的举举例就能得每一页应该从哪里开始查
即:
int start = (currentPage - 1) * pageSize
public class RouteServiceImpl implements RouteService {
private RouteDao routeDao = new RouteDaoImpl();
/**
* 分页查询
*
* @param currentPage
* @param pageSize
* @param condition
* @return
*/
@Override
public PageBean<User> pageQuery(int currentPage, int pageSize, Map<String, String[]> condition) {
//创建pageBean对象
PageBean<User> pageBean = new PageBean<User>();
//设置参数
pageBean.setCurrentPage(currentPage);
pageBean.setPageSize(pageSize);
//调用dao查询总记录数
int totalCount = routeDao.findTotalCount(condition);
pageBean.setTotalCount(totalCount);
//调用dao查询List集合
int start = (currentPage - 1) * pageSize;
List<User> list = routeDao.findByPage(start, pageSize, condition);
pageBean.setList(list);
//计算总页码
int totalPage = (totalCount % pageSize) == 0 ? totalCount / pageSize : (totalCount / pageSize + 1);
pageBean.setTotalPage(totalPage);
return pageBean;
}
}
(4) Dao 代码
/**
* 根据 start pageSize 查询当前页的数据集合
*
* @param start
* @param pageSize
* @return
*/
@Override
public List<User> findByPage(int start, int pageSize) {
String sql = "SELECT * FROM user_info LIMIT ? , ?";
return template.query(sql,
new BeanPropertyRowMapper<User>(User.class), start, pageSize);
}
(三) 前端实现
文档载入完毕
$(function () {
//暂时的传递两个固定值
var currentPage = 1;
var pageSize = 8;
//在这里调用具体的功能方法
load(currentPage,pageSize);
});
function load(currentPage, pageSize){
//具体的回显代码,下面详细解释这里些什么
}
注意:以下代码均写在 load方法中
(1) ajax 异步提交
$.get("route/routeQuery", {currentPage:currentPage,pageSize:pageSize}, function (data){
//传递currentPage、pageSize到后端,同时回调函数返回一个data
//下面是具体代码
})
我们下面按照这个流程顺序来进行说明
(2) 数据记录数以及总页码数统计
这一步,只要后台的代码写好了,基本不会出现太大的问题的
$("#pageCount").html("共" + data.totalCount + "条记录,共" + data.totalPage + "页");
(3) 用户信息回显
在HTML 中我们使用了 代码拼接的方式实现了这种需求,这个时候返回的 list集合中的一个User的数据被遍历显示到我们的表格中
('#userInfo_table tr:gt(0)').remove();
var s = '';
for (var i = 0; i < data.list.length; i++) {
s += '<tr><td>' + '<input type="checkbox" name="checkItem"/>' + '</td><td>' + data.list[i].uid + '</td><td>' + data.list[i].username + '</td><td>' + data.list[i].nickname + '</td><td>' + data.list[i].email + '</td><td>' + data.list[i].telephone + '</td><td>'+ data.list[i].gender + '</td><td>' + data.list[i].birthday + '</td><td>'+
'<button type="submit" class="btn btn-primary" id="updateBtn"> 修改</button>' + ' ' +
'<button type="submit" class="btn btn-primary" id="deleteBtn" ">删除</button>' + '</td>';
}
$('#userInfo_table').append(s);
这样我们的数据回显就体现出来了,第一页,正好8个数据 (Ps:前面测试时删过一些所以编号非0开始)
(4) 首尾页和翻页实现
var lis = " ";
//点击首页代码
var firstPage = '<li><a href="javascript:load(1,8)">首页</a></li>';
//计算上一页的页码
var previousNum = data.currentPage - 1;
if(previousNum <= 0){
previousNum = 1;
}
//上一页翻页的具体代码
var previousPage = '<li class="threeword"><a href="javascript:load('+previousNum+',8)">«</a></li>';
lis += firstPage;
lis += previousPage;尾页以及下一页和 首页和上一页 基本是差不多的
(5) 页码的处理
如何处理页码比前面几点就要复杂一点了,我们既需要用户点击后可以显示出 正确的用户信息,其次我们又需要考虑如何保证只显示我们需要的页码左右的几个页码,总不能有多少页就显示多少个页码
我们还是需要作出规范,就像这样:
/*
一共展示8个页码,前4后3
如果前面不够4个,后面补齐8个
如果后面不足8个,前面补齐8个
*/
var start;
var end;
//总页码超过8页
if (data.totalPage < 8) {
start = 1;
end = data.totalPage;
}else{
//总页码超过8页
start = data.currentPage - 4;
end = data.currentPage + 3;
//如果前面不够4个
if (start < 1) {
start = 1;
end = start + 7;
}
//如果后面不足3个,前面补齐8个
if (end > data.totalPage ) {
end = data.totalPage;
start = end - 7;
}
}
for (var i = start; i <= end; i++) {
if (data.currentPage == i){
var li = '<li class="active"><a href="javascript:load('+i+',8)">'+ i + '</a></li>';
}else{
var li = '<li><a href="javascript:load('+i+',8)">' + i + '</a> </li>';
}
lis += li;
}
说明:上文使用了这种形式执行href="javascript:load('+i+',8)
,大家可以使用onclick自行优化一下
效果展示
首页
末页
总结
这篇文章到这里就基本结束了,这个样式是我参考某马中的一个样式布的局,使用 HTML + Ajax 替代了 JSP 然后后端的代码也对应全改写了 ,不过可以说是最简单的一种分页了,比较适合在JavaWeb阶段 刚刚接触分页的朋友们了解一下,多多少少希望能带给大家一些帮助,不喜勿喷
同样,在这里祝大家新年快乐,也希望大家都能健康平安!
结尾
如果文章中有什么不足,或者错误的地方,欢迎大家留言分享想法,感谢朋友们的支持!
如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号
在这里的我们素不相识,却都在为了自己的梦而努力 ❤
一个坚持推送原创开发技术文章的公众号:理想二旬不止