分页功能的分析与实现

1年前 (2023) 程序员胖胖胖虎阿
110 0 0

文章目录

  • 前言
  • 一、什么是分页
  • 二、分页的实现
    • 1. 分页方式
    • 2. 页面跳转的几种方式
    • 3. 分页的实现——分页工具类
      • 3.1 分页工具类——PageUtil.java
      • 3.2 相关参数
  • 三、JavaWeb项目分页实现
    • 1. 逻辑分页
      • DepartmentPage.jsp
      • SearchDepartmentPageServlet
      • DepartmentDao.java
      • DepartmentService.java
    • 2. 物理分页
      • DepartmentPage.jsp
      • SearchDepartmentPageServlet2.java
      • DepartmentDao.java
      • DepartmentService.java
  • 四、使用MyBatis框架实现分页
    • 1. 分页显示班级信息
      • 1.1 ClazzMapper.java
      • 1.2 ClazzMapper.xml
      • 1.3 TestClazz.java
    • 2. 根据条件查询学生信息进行分页
      • 2.1 StudentMapper.java
      • 2.2 StudentMapper.xml
      • TestStudentMapper.java
  • 五、使用MyBatis框架的分页插件实现分页
  • 自问自答
  • 总结

前言

这篇博客是用来记录自己所理解的分页功能,后续还会有补充,如果对内容有任何疑问,欢迎一起交流学习。


一、什么是分页

分页就是让页面可以显示指定条数的数据。通过分页功能,可以更容易的管理数据,查看数据。

通过上述描述,就可以想到,大体的分页方式就是,通过各种查询,各种处理,然后将该指定条数的数据发送到前台进行显示。

二、分页的实现

1. 分页方式

  1. 逻辑分页
    先把所有数据都查出来,然后通过Java程序处理将指定条数据发送到前端进行显示。SQL语句简单,但不适合数据量大的分页。

  2. 物理分页
    利用SQL语句查询到指定条数据,发送到前端进行显示。当数据量较大时使用物理分页效率更高,但SQL语句较为复杂。

2. 页面跳转的几种方式

  1. 显示每一页的下标,通过点击下标,跳转到对应的页面。
  2. 通过点击 首页、上一页、下一页、尾页,进行跳转
  3. 通过搜索页号进行跳转
  4. 通过select下拉列表的选择进行跳转

3. 分页的实现——分页工具类

分页工具类里定义了一些用于分页的属性和方法。主要是通过分页工具类来实现分页功能的。

3.1 分页工具类——PageUtil.java

package com.dao;

public class PageUtil {
    // 每页显示的条数 
    private int pageSize;
    // 总共的条数
    private int recordCount;
    // 当前页面 
    private int currentPage;
    //总页数
    public int pageCount;
    //上一页的最后一条记录
    private int start;
    //当前页的最后一条记录
    private int end;

    // 获取上一页的最后一条记录
    public int getStart() {
        start=(currentPage - 1) * pageSize;
        return start;
    }
    //为mysql写的 select * from table limit start,end;
    //limit是限制查询从start+1开始,最多查询end条数据
    public int getEnd() {
        end=pageSize;
        return end;
    }

    // 构造方法
    public PageUtil(int pageSize, int recordCount, int currentPage) {
        this.pageSize = pageSize;
        this.recordCount = recordCount;
        setCurrentPage(currentPage);
    }

    // 构造方法
    public PageUtil(int pageSize, int recordCount) {
        this(pageSize, recordCount, 1);
    }

    public PageUtil() {
        super();
        // TODO Auto-generated constructor stub
    }

    // 总页数
    public int getPageCount() {
        // 总条数/每页显示的条数=总页数
        int size = recordCount / pageSize;
        // 最后一页的条数
        int mod = recordCount % pageSize;
        // 看需不需要多余的页,也就是最后一页
        if (mod != 0)
            size++;
        this.pageCount=recordCount == 0 ? 1 : size;
        return this.pageCount;
    }

    // 上一页的最后一条记录数。包含,起始索引为0
    public int getFromIndex() {
        // System.out.println("from index:"+(currentPage-1) * pageSize);
        return (currentPage - 1) * pageSize;
    }

    // 本页的最后一条记录数。不包含
    public int getToIndex() {
        // System.out.println("to index:"+Math.min(recordCount, currentPage *
        // pageSize));
        return Math.min(recordCount, currentPage * pageSize);
    }

    // 得到当前页
    public int getCurrentPage() {
        return currentPage;
    }

    // 设置当前页
    public void setCurrentPage(int currentPage) {
        int validPage = currentPage <= 0 ? 1 : currentPage;
        validPage = validPage > getPageCount() ? getPageCount() : validPage;
        this.currentPage = validPage;
    }

    // 得到每页显示的条数
    public int getPageSize() {
        return pageSize;
    }

    // 设置每页显示的条数
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    // 得到总共的条数
    public int getRecordCount() {
        return recordCount;
    }

    // 设置总共的条数
    public void setRecordCount(int recordCount) {
        this.recordCount = recordCount;
    }

}

3.2 相关参数

  1. 记录总数:recordCount = 通过后台查询数据库获取「一条数据就是一条记录」
  2. 页面总数:pageCount = recordCount % pageSize == 0 ? (recordCount / pageSize):(recordCount / pageSize + 1)
    如果recordCount % pageSize != 0,说明最后一页还多余出几条数据,所以就需要新增一页来存放
  3. 页面大小:pageSize = 自己设定一页到底要显示多少条数据
  4. 当前页号:currentPage = 默认值是1,是前台需要向后台传递的参数之一
  5. 前一页:currentPage - 1
  6. 后一页:currentPage + 1
  7. 上一页的最后一条记录数:start = pageSize * (currentPage - 1)
  8. 当前页的最后一条记录数:end = min(pageCount, pageSize* currentPage)
    如果该页不是最后一页,当前页最后一条记录数一定是 currentPage * pageSize
    如果该页是最后一页,当前页最后一条记录数最大就是 currentPage * pageSize;如果不是满页,则该页的最后一条记录数就是总记录数
    所以要取最小值

最重要的三个参数:记录总数、页面大小、当前页号
通过记录总数就可以知道一共有多少条数据
通过页面大小就可以知道一共有多少个页面
通过当前页号+页面大小,就可以知道当前页有哪些内容,前一页有哪些内容,后一页有哪些内容

三、JavaWeb项目分页实现

案例:对查询到的部门信息进行分页,每页显示3条数据。前台页面 DepartmentPage.jsp,后台页面 SearchDepartmentPageServlet

1. 逻辑分页

  1. DepartmentPage.jsp 作用:
    ①用于显示分页后的部门信息
    ②向后台传递「要查询什么信息——部门信息」和「当前页面的页号」
  2. SearchDepartmentPageServlet 作用:
    ①查询所有部门的信息,将指定区间的部门信息传递给前台。(连接数据库后查询到所有的部门信息,然后通过Java代码将指定区间的数据发送到前台显示)

DepartmentPage.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set value="${pageContext.request.contextPath}" var="path"></c:set>
<html>
<head>
    <title>Title</title>

    <%--引入jQuery库--%>
    <script src="${path}/js/jquery.js"></script>
    <%--定义js脚本,向后台传递当前页号和部门名称--%>
    <script>
        function formSubmit(currentPage) {
            $('input[name="currentPage"]').val(currentPage);
            $('form:first').submit();
        }
    </script>
</head>
<body>
    <%--通过查询来显示指定数据--%>
    <form action="${path}/SearchDepartmentPageServelt">
        <label>部门名称</label>
        <%--查询条件--%>
        <input type="text" name="departmentName" value="${param.departmentName}">
        <%--当前页数--%>
        <input type="hidden" name="currentPage" value="1">
        <input type="submit" value="查找">
    </form>

    <%--要显示的部门信息--%>
    <table>
        <tr>
            <th>部门编号</th>
            <th>部门名称</th>
            <th>部门地址</th>
        </tr>
        <%--这里根据后台的查询数据循环生成表格里的数据--%>
        <c:forEach items="${departments}" var="department">
            <tr>
                <th>${department.departmentId}</th>
                <th>${department.departmentName}</th>
                <th>${department.locationName}</th>
            </tr>
        </c:forEach>
    </table>

    <%--数据显示--%>
    <div>
        <label>共${pageUtil.recordCount}条数据,每页显示${pageUtil.pageSize}条数据,共${pageUtil.pageCount}页,当前是第${pageUtil.currentPage}页</label>
    </div>

    <%--用来进行页面跳转--%>
    <%--第一次查询后就已经获取到pageUtil对象,然后就可以使用pageUtil对象里的属性了--%>
    <div>
        <a href="#" onclick="formSubmit(1)" id="first">首页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" onclick="formSubmit(${pageUtil.currentPage - 1})" id="prev">上一页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" onclick="formSubmit(${pageUtil.currentPage + 1})" id="next">下一页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" onclick="formSubmit(${pageUtil.pageCount})" id="last">尾页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <label>跳转到</label>
        <%--输入框填写页数--%>
        <input type="text" id="pageNum">
        <%--根据输入框中的页数,进行跳转--%>
        <input type="button" value="Go" onclick="formSubmit($('#pageNum').val())">
    </div>
</body>
</html>

SearchDepartmentPageServlet

package com.servlets;

import com.dao.PageUtil;
import com.service.DepartmentService;
import com.vo.Department;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebServlet(name = "SearchDepartmentPageServlet", value = "/SearchDepartmentPageServlet")
public class SearchDepartmentPageServlet extends HttpServlet {
    private DepartmentService departmentService = new DepartmentService();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 从jsp页面接收到的参数
        String departmentName = request.getParameter("departmentName");
        int currentPage = Integer.parseInt(request.getParameter("currentPage"));
        // 查询到所有的部门信息
        List<Department> departmentList = departmentService.selectByDepartmentName(departmentName);
        // 创建页面划分对象
        PageUtil pageUtil = new PageUtil(3, departmentList.size(), currentPage);
        // 创建一个部门集合,用来存放要显示的部门信息。(之前是将获取到的集合对象直接转发,现在要从对象中获取指定条数的对象)
        List<Department> departments = new ArrayList<>();
        for (int i = pageUtil.getFromIndex(); i < pageUtil.getToIndex(); i++) {
            departments.add(departmentList.get(i));
        }

        // 将部门和页面工具对象请求转发到指定的页面
        request.setAttribute("departments", departments);
        request.setAttribute("pageUtil", pageUtil);
        request.getRequestDispatcher("DepartmentPage.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

DepartmentDao.java

// 通过名字查询部门
	public List<Department> selectByDepartmentName(String departmentName){
		String sql = "SELECT * FROM departments WHERE DEPARTMENT_NAME LIKE ?";
		Object[] objects = {"%"+departmentName+"%"};
		RowMapper<Department> rm = (rs)->{
			Department department = new Department();
			try {
				department.setDepartmentId(rs.getInt("DEPARTMENT_ID"));
				department.setDepartmentName(rs.getString("DEPARTMENT_NAME"));
				department.setLocationName(rs.getString("LOCATION_NAME"));
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return department;
		};
		return Dbutil.executeQuery(sql, objects, rm);
	}

DepartmentService.java

private DepartmentDao departmentDao = new DepartmentDao();
	// 通过名字查询部门
	public List<Department> selectByDepartmentName(String departmentName){
		return departmentDao.selectByDepartmentName(departmentName);
	}

2. 物理分页

  1. DepartmentPage.jsp 作用:
    ①用于显示分页后的部门信息
    ②向后台传递「要查询什么信息——部门信息」和「当前页面的页号」
  2. dao层的作用:
    ①通过SQL语句查询要显示在页面的部门信息
    ②也需要通过SQL语句查询部门信息的总记录数
  3. SearchDepartmentPageServlet 作用:
    ①负责将要显示的部门信息发送给前台
    ②当然也需要将总记录数发送给前台,用于分页计算和前台显示

DepartmentPage.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set value="${pageContext.request.contextPath}" var="path"></c:set>
<html>
<head>
    <title>Title</title>

    <%--引入jQuery库--%>
    <script src="${path}/js/jquery.js"></script>
    <%--定义js脚本,向后台传递当前页号和部门名称--%>
    <script>
        function formSubmit(currentPage) {
            $('input[name="currentPage"]').val(currentPage);
            $('form:first').submit();
        }
    </script>
</head>
<body>
    <%--通过查询来显示指定数据--%>
    <form action="${path}/SearchDepartmentPageServelt2">
        <label>部门名称</label>
        <%--查询条件--%>
        <input type="text" name="departmentName" value="${param.departmentName}">
        <%--当前页数--%>
        <input type="hidden" name="currentPage" value="1">
        <input type="submit" value="查找">
    </form>

    <%--要显示的部门信息--%>
    <table>
        <tr>
            <th>部门编号</th>
            <th>部门名称</th>
            <th>部门地址</th>
        </tr>
        <%--这里根据后台的查询数据循环生成表格里的数据--%>
        <c:forEach items="${departments}" var="department">
            <tr>
                <th>${department.departmentId}</th>
                <th>${department.departmentName}</th>
                <th>${department.locationName}</th>
            </tr>
        </c:forEach>
    </table>

    <%--数据显示--%>
    <div>
        <label>共${pageUtil.recordCount}条数据,每页显示${pageUtil.pageSize}条数据,共${pageUtil.pageCount}页,当前是第${pageUtil.currentPage}页</label>
    </div>

    <%--用来进行页面跳转--%>
    <%--第一次查询后就已经获取到pageUtil对象,然后就可以使用pageUtil对象里的属性了--%>
    <div>
        <a href="#" onclick="formSubmit(1)" id="first">首页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" onclick="formSubmit(${pageUtil.currentPage - 1})" id="prev">上一页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" onclick="formSubmit(${pageUtil.currentPage + 1})" id="next">下一页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="#" onclick="formSubmit(${pageUtil.pageCount})" id="last">尾页</a>&nbsp;&nbsp;&nbsp;&nbsp;
        <label>跳转到</label>
        <%--输入框填写页数--%>
        <input type="text" id="pageNum">
        <%--根据输入框中的页数,进行跳转--%>
        <input type="button" value="Go" onclick="formSubmit($('#pageNum').val())">
    </div>
</body>
</html>

SearchDepartmentPageServlet2.java

package com.servlets;

import com.dao.PageUtil;
import com.service.DepartmentService;
import com.vo.Department;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.List;

@WebServlet(name = "SearchDepartmentPageServelt2", value = "/SearchDepartmentPageServelt2")
public class SearchDepartmentPageServelt2 extends HttpServlet {
    private DepartmentService departmentService = new DepartmentService();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String departmentName = request.getParameter("departmentName");
        int currentPage = Integer.parseInt(request.getParameter("currentPage"));

        PageUtil pageUtil = new PageUtil(3, departmentService.selectByDepartmentName(departmentName).size(), currentPage);
        List<Department> departments = departmentService.selectLimitDepartment(departmentName, pageUtil.getStart(), pageUtil.getEnd());

        request.setAttribute("pageUtil", pageUtil);
        request.setAttribute("departments", departments);
        request.getRequestDispatcher("DepartmentPage.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

DepartmentDao.java

// 通过名字查询部门
	public List<Department> selectByDepartmentName(String departmentName){
		String sql = "SELECT * FROM departments WHERE DEPARTMENT_NAME LIKE ?";
		Object[] objects = {"%"+departmentName+"%"};
		RowMapper<Department> rm = (rs)->{
			Department department = new Department();
			try {
				department.setDepartmentId(rs.getInt("DEPARTMENT_ID"));
				department.setDepartmentName(rs.getString("DEPARTMENT_NAME"));
				department.setLocationName(rs.getString("LOCATION_NAME"));
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return department;
		};
		return Dbutil.executeQuery(sql, objects, rm);
	}
	
// *****************这个是重点*****************
// 通过名字查询部门
	public List<Department> selectLimitDepartment(String departmentName, int start, int end){
		String sql = "SELECT * FROM departments WHERE DEPARTMENT_NAME LIKE ? limit ?, ?";
		Object[] objects = {"%"+departmentName+"%", start, end};
		RowMapper<Department> rm = (rs)->{
			Department department = new Department();
			try {
				department.setDepartmentId(rs.getInt("DEPARTMENT_ID"));
				department.setDepartmentName(rs.getString("DEPARTMENT_NAME"));
				department.setLocationName(rs.getString("LOCATION_NAME"));
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return department;
		};
		return Dbutil.executeQuery(sql, objects, rm);
	}

DepartmentService.java

private DepartmentDao departmentDao = new DepartmentDao();
	// 通过名字查询部门
	public List<Department> selectByDepartmentName(String departmentName){
		return departmentDao.selectByDepartmentName(departmentName);
	}

	// 通过名字查询部门
	public List<Department> selectLimitDepartment(String departmentName, int start, int end){
		return departmentDao.selectLimitDepartment(departmentName, start, end);
	}

四、使用MyBatis框架实现分页

这里没有使用前端页面来展示分页后的内容,而是通过在控制台打印查询到的分页信息来显示分页内容。下面用两个案例来介绍分页功能。

1. 分页显示班级信息

这里使用的是物理分页,需要做三件事来实现分页:
①从数据库中查出要显示数据的总记录数
②从数据库中查出要显示的数据集合
③分页工具类对象指定页面大小、获取①中的页面总数、指定当前是第几页即可

1.1 ClazzMapper.java

// 查询到记录总数,就是一共有多少条班级信息
int selectCount();
// 根据分页工具类进行分页,获取到分页后要显示的班级信息
List<Clazz> selectPage(@Param("page") PageUtil pageUtil);

1.2 ClazzMapper.xml

<!--查询数据库一共有多少条记录-->
<select id="selectCount" resultType="int">
    select count(*) from clazz
</select>

<!--物理分页-->
<select id="selectPage" resultType="com.bean.Clazz">
    select <include refid="base_info"></include> from clazz
    limit #{page.start}, #{page.end}
</select>

1.3 TestClazz.java

	@Test
    public void testSelectPage() {
        // 这里输出分页信息
        int count = this.clazzService.selectCount();
        PageUtil pageUtil = new PageUtil(3, count, 3);
        System.out.println("共"+pageUtil.pageCount+"页");
        System.out.println("当前是第"+pageUtil.getCurrentPage()+"页");
        System.out.println("共有"+pageUtil.getRecordCount()+"条数据");
        // 这里输出分页后的班级信息
        List<Clazz> clazzes = this.clazzService.selectPage(pageUtil);
        for (int i=0; i<clazzes.size(); i++) {
            Clazz clazz = clazzes.get(i);
            System.out.println(clazz.getId() + "\t" + clazz.getClassname());
        }
    }

2. 根据条件查询学生信息进行分页

这里也是用的是物理分页,需要做三件事来实现分页:
①查询满足条件的记录总数
②查询满足条件的要显示的数据集合
③分页工具类对象中指定页面大小记录总数当前页数

2.1 StudentMapper.java

	// 根据条件查询学生的数量(不需要使用爱好做条件),查询总记录数
    int selectCount(Student student);
    // 根据条件查询学生的信息,实现分页
    List<Student> selectPage(@Param("student") Student student, @Param("page") PageUtil page);

2.2 StudentMapper.xml

 <!--根据条件查询出记录总数-->
  <select id="selectCount" resultType="int">
      select count(*) from student
      <where>
        <if test="name != null">
          name like "%"#{name}"%"
        </if>
        <if test="sex != null">
          and sex = #{sex}
        </if>
        <if test="birthday != null">
          and birthday = #{birthday}
        </if>
        <if test="age != null and age > 0">
          and age = #{age}
        </if>
        <if test="classid != null and classid > 0">
          and classid = #{classid}
        </if>
      </where>
  </select>
  
  <!--根据条件查询出要显示的数据集合-->
  <select id="selectPage" resultMap="baseMap">
    select <include refid="Base_Column_List"></include> from student
    <where>
      <if test="student.name != null">
        name like "%"#{student.name}"%"
      </if>
      <if test="student.sex != null">
        and sex = #{student.sex}
      </if>
      <if test="student.birthday != null">
        and birthday = #{student.birthday}
      </if>
      <if test="student.age != null and student.age > 0">
        and age = #{student.age}
      </if>
      <if test="student.classid != null and student.classid > 0">
        and classid = #{student.classid}
      </if>
    </where>
    limit #{page.start}, #{page.end}
  </select>

TestStudentMapper.java

	public void testSelectPage() {
        // 自定义查询条件
        Student student = new Student();
        student.setName("a");
        student.setSex("man");
        // 输出分页信息
        int count = this.studentService.selectCount(student);
        PageUtil pageUtil = new PageUtil(3, count, 2);
        System.out.println("共"+pageUtil.pageCount+"页");
        System.out.println("当前是第"+pageUtil.getCurrentPage()+"页");
        System.out.println("共有"+pageUtil.getRecordCount()+"条数据");
        // 输出要显示的数据集合
        List<Student> students = this.studentService.selectPage(student, pageUtil);
        for (int i=0; i < students.size(); i++) {
            System.out.println(students.get(i).getName() + "\t" + students.get(i).getSex());
        }
    }

五、使用MyBatis框架的分页插件实现分页

详细可点击该链接查看。

自问自答

  1. 为什么点击页号后可以跳转到不同的页面?
    其实没有跳转到不同的页面,而是一直都在同一个页面,只是显示的内容不同。根据页号查询到的信息不同,所以在页面上显示的内容也不同。
  2. 物理分页和逻辑分页的联系与区别?
    联系:都是根据页号指定区间的数据发送到前台显示
    区别:物理分页是在dao层SQL语句中查询到指定区间的数据,并将指定区间的数据发送给前端;逻辑分页在dao层SQL语句中查询到的是所有的信息,然后还需要通过Java代码获取到指定区间的数据发送给前端。

总结

以上就是对分页功能的记录。需要分清「逻辑分页」和「物理分页」联系和区别。逻辑分页SQL语句可能相对简单一点,但不适用于数据量极大的情况。数据量较大的时候,需要使用物理分页。重点理解分页工具类,然后结合我提供的思路和核心代码,基本上就可以实现分页功能了。

版权声明:程序员胖胖胖虎阿 发表于 2023年9月4日 上午5:48。
转载请注明:分页功能的分析与实现 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...