专栏首页理想二旬不止JavaWeb之简单分页查询分析及代码

JavaWeb之简单分页查询分析及代码

作者的话:

首先祝大家新年快乐,同样希望大家都可以健健康康的度过这次疫情,然后我想解释一下为什么停更长达一两个月,去年总是可能是熬夜生活作息不太规律,总是偏头痛,程度还挺重,已经影响自己的正常工作和学习,后来配合休息和药,才基本恢复了,上个学期末学校的事也是多了一些,很多时间都用在了课业或者看一些技术书上,所以停更了算挺久,非常抱歉,很感谢即使停更,大家也没有离我而去,从今天起,我接着开始更新一些文章,希望我粗浅的技术能给大家一些切实的帮助,非常欢迎大家用公众号后台,微信或者邮件的方式(文末有联系方式)与我交流,再次感谢大家!

往事随风尽飘散,未来美好盼可期

技术涵盖(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>' + '&nbsp' + 
            '<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)">&laquo;</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阶段 刚刚接触分页的朋友们了解一下,多多少少希望能带给大家一些帮助

同样,在这里祝大家新年快乐,也希望大家都能健康平安!

本文分享自微信公众号 - 理想二旬不止(ideal-20),作者:ideal20

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 第三阶段-Java常见对象:【第八章 System类】

    System.gc() 可用于垃圾回收.当使用System.gc() 回收某个对象所占用的内存之前,通过要求程序调用适当的方法来清理资源,在没有明确指定资源清理...

    BWH_Steven
  • 【万字长文】Spring AOP 层层递进轻松入门 !

    Tips:如果想要快速查阅的朋友,可以直接跳转到 初识AOP(Spring 程序)这一大节

    BWH_Steven
  • 第三阶段-Java常见对象:【第十一章 Date、DateFormat和Calendar类】

    可以进行日期和字符串的格式化和解析,但是由于是抽象类,所以使用具体子类SimpleDateFormat。

    BWH_Steven
  • python创建tcp服务端和客户端

    py3study
  • 洛谷P2147 [SDOI2008]Cave 洞穴勘测

    题目描述 辉辉热衷于洞穴勘测。 某天,他按照地图来到了一片被标记为JSZX的洞穴群地区。经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道...

    attack
  • 数据结构与算法笔试面试题整理

    b.对每一对相邻的元素做同样的工作,从开始的第一对一致到结尾的最后一对,经过这一步,最后的元素将是最大值;

    Zoctopus
  • 表的应用——排序与描述多项式排序多项式ADTGO语言笔记

    排序 朴素排序 在链表建立的过程中可以直接完成排序功能,即建立一个新链表并将源数据一个一个存进新链表中,每个元素存储的位置在小于这个元素的节点和大于这个元素的节...

    月见樽
  • Golang Leetcode 238. Product of Array Except Self.go

    版权声明:原创勿转 https://blog.csdn.net/anakinsun/article/details/89055013

    anakinsun
  • 【Flutter 专题】78 图解 Android Native 集成 FlutterBoost 小尝试 (一)

    和尚前几天刚将历史项目升级至 AndroidX 并接入 Flitter Module,接下来和尚准备采用 flutter_boost 进行 Nati...

    阿策
  • Java Varargs 可变参数使用

    "Varargs"是 “variable number of arguments”的意思。有时候也被简单的称为 “variable arguments”。

    HoneyMoose

扫码关注云+社区

领取腾讯云代金券