分页原理及其手动实现

分页概念

今天分享给各位小伙伴的是javaweb分页,主要从分页的原理以及实现进行介绍。首先要说的是什么是分页,大家都上过网,不知道各位发现一个小细节没有,就是在一个网页的最下面有一栏显示上一页、下一页等,就像下图这样。
在这里插入图片描述
这个就是所谓的分页查询。

分页原理

那么分页查询是怎么实现的呢?下面我们就从它的实现原理进行深入介绍。大家可以观察一下上图中有哪些元素。我们不妨假设这张图片就是一个网页(事实上就是一个网页),那么它得有总数据条数、总页数、当前所在的页数、页面大小(一页显示多少条数据)以及页面元素(即页面展示出来的是什么,上图中的页面元素就是eclipse的有关搜索结果)。
下面对细节进行讲解,先上图
在这里插入图片描述
为了方便介绍,我们将记录总数记作totalCount、当前所在页数currentPage、页面大小pageSize、总页数totalPage以及页面元素集合List,这五个量是实现分页的关键,其中当前所在页数和页面大小可以由用户指定(从前台传来),而其它的三个量都是存储在数据库(DBMS)中,要想让前台页面显示出分页的效果,就必须让前台知道总记录数是多少、总页数是多少、学生集合是什么、当前页是多少以及页面大小,只有前台知道了这些才能显示出如图一那样的效果。
在本例中我们先将页面大小定为固定值(事实上也推荐这么做),下面引入几个结论:

  1. 总记录数:使用select * from Xxx这条sql语句即可搞定
  2. 学生集合:使用mysql分页sql(limit)
  3. 总页数:进行推导
    当总记录数是15条时,页面大小固定为5,那么总页数=15/5=3
    当总记录数是16条时,页面大小固定为5,那么总页数=16/5+1=4
    结论:总页数=(总记录%/页面大小)==0?(总记录数/页面大小):(总记录数/页面大小)+1

分页实现

我们可以将查询总记录数的方法定义在DBUtil中以便后期的复用,如下

// 查询总数
    public static int getTotalCount(String sql,Object[] params){
        int count = -1;
        rs = executeQuery(sql, params);
        try {
            if (rs.next()){
                count = rs.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll(rs,pstmt,connection);
        }
        return count;
    }

这样我们就直接在数据访问层调用即可。下面是数据访问层的源码:

@Override
    public int getTotalCount() {
        String sql = "SELECT COUNT(*) FROM student";
        return DBUtil.getTotalCount(sql,null);
    }

    @Override
    public List<Student> queryStudentByPage(int currentPage,int pageSize) {

        /**
         * MySQL分页SQL
         * SQL语句:SELECT FROM Xxx(表名) LIMIT ?(起始行号),?(页面大小);
         * 推导一下第一个"?"的值
         * 假设页面大小固定为5,那么有
         *                        页码               起始行               页面大小
         *                        1                 0                   5
         *                        2                 5                   5
         *                        3                 10                  5
         *                 分页SQL结论:第一个"?"=(当前页码-1)*页面大小
         */

        // 书写分页SQL
        String sql = "SELECT * FROM student LIMIT ? , ?";
        Object[] params = {(currentPage-1)*pageSize,pageSize};
        // 创建List集合
        List<Student> students = new ArrayList<>();
        Student student = null;
        // 调用DBUtil通用查询方法
        ResultSet rs = null;
        rs = DBUtil.executeQuery(sql,params);
        try {
            // 取出学生信息存入集合
            while (rs.next()){
                int no = rs.getInt("sno");
                String name = rs.getString("sname");
                int age = rs.getInt("sage");
                student = new Student(no,name,age);
                students.add(student);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return students;
    }

下面给出service层源码:

@Override
    public List<Student> queryStudentByPage(int currentPage, int pageSize) {
        return studentDao.queryStudentByPage(currentPage, pageSize);
    }

    @Override
    public int getTotalCount() {
        return studentDao.getTotalCount();
    }

最后是表示层后台:

@WebServlet("/QueryStudentByPage")
public class QueryStudentByPage extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 接收前台传来的数据
        req.setCharacterEncoding("UTF-8");
        String cPage = req.getParameter("currentPage");
        // 第一次进入servlet肯定拿不到当前页,会报null
        if (cPage==null){
            cPage="1";// 第一次进入默认为第一页
        }
        int currentPage = Integer.parseInt(cPage);
        int pageSize = 5;// 一般建议为固定值
        // 创建业务逻辑层对象
        IStudentService service = new StudentServiceImpl();
        // 调用业务逻辑层方法
        int totalCount = service.getTotalCount();// 获取数据总条数
        List<Student> students = service.queryStudentByPage(currentPage, pageSize);// 获取学生集合
        int totalPage = totalCount%pageSize==0?(totalCount/pageSize):(totalCount/pageSize)+1;
        // 组装PageBean对象
        PageBean pageBean = new PageBean();
        pageBean.setCurrentPage(currentPage);
        pageBean.setPageSize(pageSize);
        pageBean.setTotalCount(totalCount);
        pageBean.setStudents(students);
        pageBean.setTotalPage(totalPage);
        System.out.println(pageBean);
        // 将PageBean对象放到request对象作用域中
        req.setAttribute("pageBean",pageBean);
        req.getRequestDispatcher("show.jsp").forward(req,resp);// 转发至前台进行显示
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req,resp);
    }
}

我们知道在经典的三层架构中,表示层后台的任务就是调用service层,但我认为service层最重要的任务就是将数据组装到实体类中,让其能够在三层中间传递数据。
学生实体类:

// 实体类
public class Student {
    // 属性
    private int sno;
    private String sname;
    private int sage;

    // 构造器
    public Student() {
    }

    public Student(String sname, int sage) {
        this.sname = sname;
        this.sage = sage;
    }

    public Student(int sno, String sname, int sage) {
        this.sno = sno;
        this.sname = sname;
        this.sage = sage;
    }

    // setter and getter
    public int getSno() {
        return sno;
    }

    public void setSno(int sno) {
        this.sno = sno;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public int getSage() {
        return sage;
    }

    public void setSage(int sage) {
        this.sage = sage;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sno=" + sno +
                ", sname='" + sname + '\'' +
                ", sage=" + sage +
                '}';
    }
}

PageBean实体类:

/**
 * 分页实体类
 * 1.本程序在三层案例的基础上增加一个分页功能
 * 2.分页需要的元素
 *  -totalCount:数据总数
 *  -currentPage:当前页,一般由前台传来(用户自定义)
 *  -pageSize:页面大小,一般建议固定值
 *  -totalPage:总页数,计算过程如下
 *      若totalCount%pageSize==0,则totalPage=totalCount/pageSize,否则totalPage=(totalCount/pageSize)+1
 *  -List<T>:页面元素集合,用于存储从数据库中查询到的元素
 */
public class PageBean {
    // 属性
    private int totalCount;
    private int pageSize;
    private int currentPage;
    private int totalPage;
    private List<Student> students;

    // 构造器
    public PageBean() {
    }

    public PageBean(int totalCount, int pageSize, int currentPage, int totalPage, List<Student> students) {
        this.totalCount = totalCount;
        this.pageSize = pageSize;
        this.currentPage = currentPage;
        this.totalPage = totalPage;
        this.students = students;
    }

    // setter and getter
    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }
}

前台代码:

<%@ page import="java.util.List" %>
<%@ page import="com.mamusha.entity.Student" %>
<%@ page import="com.mamusha.entity.PageBean" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>学生信息列表</title>
</head>
<body>

    <!--增加成功与否提示-->
    <%
        // 接收AddStudentServlet传来的提示标记
        String error = (String) request.getAttribute("error");
        // 进行判断
        if (error!=null){
            if (error.equals("noError")){
                // 增加成功
                out.print("增加成功!");
            }else if (error.equals("error")){
                // 增加失败
                out.print("增加失败!");
            }
        }

    %>

    <!--用一个表格显示学生信息-->
    <table border="1px">

        <tr>
            <th>学号</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>操作</th>
        </tr>

        <!--学生信息应该是动态的,不应该写死,学生信息在QueryAllStudentServlet后台里,必须调用该类拿到学生信息-->
        <%
            // 接收后台传过来的学生信息
            PageBean pageBean = (PageBean) request.getAttribute("pageBean");
            // 循环(使表格动态变化)
            for (Student student : pageBean.getStudents()){
        %>
        <tr>
            <td><a href="QueryStudentBySnoServlet?sno=<%=student.getSno()%>"><%=student.getSno()%></a></td>
            <td><%=student.getSname()%></td>
            <td><%=student.getSage()%></td>
            <td><a href="DeleteStudentBySnoServlet?sno=<%=student.getSno()%>">删除</a></td>
        </tr>
        <%
            }
        %>
    </table>
    <a href="add.jsp">新增</a><br/>

    <!--对页码进行控制,例如当前在第一页的话应该看不到上一页,当前页是尾页的话看不到下一页-->
    <%
        if (pageBean.getCurrentPage()==1){ // 首页只能看到下一页和尾页
    %>
            <a href="QueryStudentByPage?currentPage=<%=pageBean.getCurrentPage()+1%>">下一页</a>
            <a href="QueryStudentByPage?currentPage=<%=pageBean.getTotalPage()%>">尾页</a>
    <%
        }else if (pageBean.getCurrentPage()==pageBean.getTotalPage()){ // 尾页只能看到首页和上一页

    %>
            <a href="QueryStudentByPage?currentPage=1">首页</a>
            <a href="QueryStudentByPage?currentPage=<%=pageBean.getCurrentPage()-1%>">上一页</a>
    <%
        }else { // 其它首页、下一页、上一页、尾页均可看到

    %>
            <a href="QueryStudentByPage?currentPage=1">首页</a>
            <a href="QueryStudentByPage?currentPage=<%=pageBean.getCurrentPage()-1%>">上一页</a>
            <a href="QueryStudentByPage?currentPage=<%=pageBean.getCurrentPage()+1%>">下一页</a>
            <a href="QueryStudentByPage?currentPage=<%=pageBean.getTotalPage()%>">尾页</a>
    <%
        }
    %>


</body>
</html>

xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <welcome-file-list>
        <!--将QueryStudentByPage放在第一位,否则show.jsp页在接收后台数据时会报空指针异常,很简单,因为此时QueryStudentByPage还没被加载-->
        <welcome-file>QueryStudentByPage</welcome-file>
        <welcome-file>show.jsp</welcome-file>
    </welcome-file-list>
</web-app>

下面是部署在tomcat中的访问项目运行图:
在这里插入图片描述
部分实现细节可能讲的并不是那么通俗易懂,还需要读者自己多动手,在实践中去慢慢摸索,感谢大家的阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值