Servlet查询分页物理分页
逻辑分页
一次从数据库中把所有数据读取到内存中, 在内存中进行数据切分,然后传到前端页面。
物理分页
在查询数据库时就按照页数只取需要的数据,不在内存中处理
分页的主要思路
物理分页
思路
- 前端页面发起请求,要展示一页数据。 把
当前要展示的页码
、每页展示条数
传给后台 - 根据
要展示的页码数
、每页展示条数
去数据库中查询对应页码、条数的数据 - 后台需要额外查询
总数据条数
, 根据总数据条数
和每页展示条数
计算出总页数
一共涉及到的参数
-
当前展示页码: currentPage (由前端页面决定)
-
每页展示条数: pageSize (由前端页面决定)
-
总数据条数: totalCount (从数据库查询得来)
-
总页数: totalPage (由totalCount 和 pageSize计算而来)
比如说: 总记录条数是100条,每页展示10条,一共多少页? — 10
再比如说: 总记录条数是102条,每页展示10条,一共多少页? — 11
也就是说:
totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
-
当前页对应的数据: List data; (从数据库查询得来)
分页语句
案例:假设我要从MySQL中student
表中查询第2
页的数据,每页展示20
条
第1页: 01 ~ 20
第2页: 21 ~ 40
第3页: 41 ~ 60
…
要显示第n页的范围公式: (n-1)*pageSize+1 ~ n*pageSize
结合MySQL中的语法,得到MySQL中分页公式:
-- limit后面的两个参数代表:
-- 第一个: 跳过多少条
-- 第二个: 取多少条数据
-- MySQL中分页语法:
select * from student limit (n-1)*pageSize, pageSize;
物理分页实现代码
- 导包导入到/WebContent/WEB-INF/lib/
druid-1.1.10.jar
jstl-1.2.jar
mysql-connector-java-5.1.47.jar
index.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<a href="physical?currentPage=1&pageSize=20">物理分页</a>
<br />
</body>
</html>
在utils包中创建工具类
- 创建连接数据库的utils类
package com.lanou3g.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
/**
* 数据库相关工具类
*
*
*/
public class DBTools {
//定义常量
private static String PROP_DRIVERCLASSNAME = "com.mysql.jdbc.Driver";
private static String PROP_URL = "jdbc:mysql://localhost:3306/jsp_01?characterEncoding=utf8";
private static String PROP_USERNAME = "root";
private static String PROP_PASSWORD = "123456";
private static DruidDataSource ds;
static {
try {
Class.forName(JDBC_DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据源
* @return
*/
public static DataSource getDataSource() {
if(ds != null) {
return ds;
}
DruidDataSource ds = new DruidDataSource();
ds.setUrl(JDBC_URL);
ds.setUsername(JDBC_USERNAME);
ds.setPassword(JDBC_PASSWORD);
ds.setMaxActive(5);
ds.setMinIdle(1);
return ds;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public static void main(String[] args) throws SQLException {
DataSource ds = DBTools.getDataSource();
System.out.println(ds.getConnection());
}
}
bean包中创建实体类
根据数据库创建student实体类
package com.lanou3g.bean;
import java.sql.Timestamp;
public class Student {
private Integer id;
private String sname;
private String subject;
private String tel;
private String addr;
private Timestamp createtime;
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public Timestamp getCreatetime() {
return createtime;
}
public void setCreatetime(Timestamp createtime) {
this.createtime = createtime;
}
}
因为要获取到五个参数
当前页(currentPage) 当前页中数据(data) 每页显示多少条(pageSize) 总数据条数(totalCount) 总页码数(totalPage)
注意:总页码数是根据数据库有多少数据来显示的所以它不用生成setter方法,它只需要生成getter方法就成了然后再算出来总共多少页
所以要在bean包中创建一个分页的实体类把上面的放在一个类中
package com.lanou3g.bean;
import java.util.List;
public class Paganation<T> {
/**
* 当前要展示的页码
*/
private Integer currentPage;
/**
* 每页展示数据的条数
*/
private Integer pageSize;
/**
* 总数据条数
*/
private Integer totalCount;
/**
* 总页码数
*/
private Integer totalPage;
/**
* 当前页数据
*/
private List<T> data;
/**
* 计算总页数
* @return
*/
public Integer getTotalPage() {
int totalPage = getTotalCount()/getPageSize();
if(getTotalCount()%getPageSize()!=0) {
totalPage++;
}
return totalPage;
}
/**
* 返回是否有上一页
* @return
*/
public boolean hasPre() {
return getCurrentPage()>1;
}
/**
* 返回是否有下一页
* @return
*/
public boolean hasNext() {
return getCurrentPage()<getTotalPage();
}
public Integer getCurrentPage() {
return currentPage;
}
public void setCurrentPage(Integer currentPage) {
this.currentPage = currentPage;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
}
根据分页的思路创建servlet
创建PhysicalPageServlet
package com.lanou3g.web;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.lanou3g.bean.Paganation;
import com.lanou3g.bean.Student;
import com.lanou3g.dao.StudentDao;
/**
* 物理分页
* @author John
*
*/
@WebServlet("/physical")
public class PhysicalPageServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
StudentDao studentDao = new StudentDao();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 默认展示第一页
int currentPage = 1;
try {
currentPage = Integer.parseInt(request.getParameter("currentPage"));
} catch (NumberFormatException e) {
}
// 默认每页展示20条
int pageSize = 20;
try {
pageSize = Integer.parseInt(request.getParameter("pageSize"));
} catch (NumberFormatException e) {
}
// 将前台传递过来的参数封装page对象中
Paganation<Student> page = new Paganation<Student>();
page.setCurrentPage(currentPage);
page.setPageSize(pageSize);
// 调用dao层方法,完成物理分页查询, 分页参数和数据会在dao方法中都封装到page对象里
studentDao.findByPhysicalPage(page);
// 将page对象放入请求域
request.setAttribute("page", page);
// 转发到学生列表页
request.getRequestDispatcher("student_list.jsp").forward(request, response);
}
}
创建连接数据库dao层包
创建一个studentdao类
package com.lanou3g.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.lanou3g.bean.Paganation;
import com.lanou3g.bean.Student;
import com.lanou3g.utils.DBTools;
/**
* Student表分页查询
*
*/
public class StudentDao {
/**
* 物理分页
*
* @param page
*/
public void findByPhysicalPage(Paganation<Student> page) {
// 从page对象中拿出前端传过来的两个参数: 当前页、页面大小
Integer currentPage = page.getCurrentPage();
Integer pageSize = page.getPageSize();
// 后台需要往page中补充剩下的三个参数: 总记录条数、当前页的数据、总页数
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 拿总记录数(totalCount): select count(*) from student;
String sql = "select count(*) as total_count from student;";
conn = DBTools.getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if(rs.next()) {
int totalCount = rs.getInt("total_count");
// 将总记录条数封装到page对象中
page.setTotalCount(totalCount);
}
rs.close();
pstmt.close();
// 第n页数据范围: (n-1)*pageSize+1 ~ n * pageSize
// MySQL写法: limit (n-1)*pageSize, pageSize
List<Student> studentList = new ArrayList<Student>();
sql = "select * from student limit ?,?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, (currentPage - 1) * pageSize);
pstmt.setInt(2, pageSize);
rs = pstmt.executeQuery();
while(rs.next()) {
Student student = new Student();
student.setId(rs.getInt("id"));
student.setSname(rs.getString("sname"));
student.setSubject(rs.getString("subject"));
student.setTel(rs.getString("tel"));
student.setAddr(rs.getString("addr"));
student.setCreatetime(rs.getTimestamp("createtime"));
studentList.add(student);
}
// 将当前页数据封装到page对象中
page.setData(studentList);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
最后前端页面接收后台返回的数据
student_list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>学生列表</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
margin: 100px auto;
}
td,th {
border: 1px solid #888;
padding: 3px 8px;
text-align: center;
}
.pageBar>a {
text-decoration: none;
border: 1px solid blue;
padding: 3px 5px;
margin-left: 10px;
color: white;
border-radius: 3px;
background-color: lightblue;
}
.pageBar>span {
border: 1px solid #999;
padding: 3px 5px;
margin-left: 10px;
color: white;
border-radius: 3px;
background-color: #ccc;
}
.pageBar {
border: none;
padding-top: 10px;
}
.pageBar>.text {
border: none;
}
</style>
</head>
<body>
<table>
<caption>学生信息表</caption>
<tr>
<th>学号</th>
<th>姓名</th>
<th>学科</th>
<th>电话</th>
<th>住址</th>
<th>入学时间</th>
</tr>
<c:forEach var="stu" items="${page.data }">
<tr>
<td>${stu.id }</td>
<td>${stu.sname }</td>
<td>${stu.subject }</td>
<td>${stu.tel }</td>
<td>${stu.addr }</td>
<td>${stu.createtime }</td>
</tr>
</c:forEach>
<tr>
<td colspan="6" class="pageBar">
<!-- 当前页大于1时,说明还有上一页,这时首页和上一页都可以点击 -->
<c:if test="${page.currentPage > 1 }">
<a href="physicalPageServlet?currentPage=1&pageSize=20">首页</a>
<a href="physical?currentPage=${page.currentPage-1 }&pageSize=20">上一页</a>
</c:if>
<!-- 当前页小于等于1时,说明已经是第一页了,这时首页和上一页都不能点击 -->
<c:if test="${page.currentPage <= 1 }">
<span>首页</span>
<span>上一页</span>
</c:if>
<span class="text">
当前页码: ${page.currentPage } / ${page.totalPage }
</span>
<!-- 当前页小于总页数时,说明还没到最后一页,这时尾页和下一页都可以点击 -->
<c:if test="${page.currentPage < page.totalPage }">
<a href="physical?currentPage=${page.currentPage+1 }&pageSize=20">下一页</a>
<a href="physical?currentPage=${page.totalPage }&pageSize=20">尾页</a>
</c:if>
<!-- 当前页大于等于总页数时,说明还已经到最后一页,这时尾页和下一页都不可以点击 -->
<c:if test="${page.currentPage >= page.totalPage }">
<span>下一页</span>
<span>尾页</span>
</c:if>
</td>
</tr>
</table>
</body>
</html>
最后的结果的就是如下所示物理分页成功