分页概念
今天分享给各位小伙伴的是javaweb分页,主要从分页的原理以及实现进行介绍。首先要说的是什么是分页,大家都上过网,不知道各位发现一个小细节没有,就是在一个网页的最下面有一栏显示上一页、下一页等,就像下图这样。
这个就是所谓的分页查询。
分页原理
那么分页查询是怎么实现的呢?下面我们就从它的实现原理进行深入介绍。大家可以观察一下上图中有哪些元素。我们不妨假设这张图片就是一个网页(事实上就是一个网页),那么它得有总数据条数、总页数、当前所在的页数、页面大小(一页显示多少条数据)以及页面元素(即页面展示出来的是什么,上图中的页面元素就是eclipse的有关搜索结果)。
下面对细节进行讲解,先上图
为了方便介绍,我们将记录总数记作totalCount、当前所在页数currentPage、页面大小pageSize、总页数totalPage以及页面元素集合List,这五个量是实现分页的关键,其中当前所在页数和页面大小可以由用户指定(从前台传来),而其它的三个量都是存储在数据库(DBMS)中,要想让前台页面显示出分页的效果,就必须让前台知道总记录数是多少、总页数是多少、学生集合是什么、当前页是多少以及页面大小,只有前台知道了这些才能显示出如图一那样的效果。
在本例中我们先将页面大小定为固定值(事实上也推荐这么做),下面引入几个结论:
- 总记录数:使用select * from Xxx这条sql语句即可搞定
- 学生集合:使用mysql分页sql(limit)
- 总页数:进行推导
当总记录数是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中的访问项目运行图:
部分实现细节可能讲的并不是那么通俗易懂,还需要读者自己多动手,在实践中去慢慢摸索,感谢大家的阅读。