分页简介
分页功能在网页中是非常常见的一个功能,其作用也就是将数据分割成多个页面来进行显示。
使用场景:
当取到的数据量达到一定的时候,就需要使用分页来进行数据分割。
当我们不使用分页功能的时候,会面临许多的问题:
客户端的问题: 如果数据量太多,都显示在同一个页面的话,会因为页面太长严重影响到用户的体验,也不便于操作,也会出现加载太慢的问题。
服务端的问题: 如果数据量太多,可能会造成内存溢出,而且一次请求携带的数据太多,对服务器的性能也是一个考验。
分页的分类
分页的实现分为真分页和假分页两种,也就是物理分页和逻辑分页。
1.真分页(物理分页):
实现原理: SELECT * FROM xxx [WHERE…] LIMIT #{param1}, #{param2}
第一个参数是开始数据的索引位置
第二个参数是要查询多少条数据
优点: 不会造成内存溢出
缺点: 翻页的速度比较慢
2.假分页(逻辑分页):
实现原理: 一次性将所有的数据查询出来放在内存之中,每次需要查询的时候就直接从内存之中去取出相应索引区间的数据
优点: 分页的速度比较快
缺点: 可能造成内存溢出
传统的分页方式
对于假分页的实现方式很简单,只需要准备一个集合保存从数据库中取出的所有数据,然后根据当前页面的码数,取出对应范围的数据显示就好了,我们这里基于物理分页来实现。
分页的原理
页面中的数据有:
结果集:通过 SQL 语句查询得来的——List
分页条中的数据有:
当前页:用户传递到后台——currentPage
总页数:计算的来——totalPage
上一页:计算的来——prePage
下一页:计算的来——nextPage
尾页:计算的来(总页数)——lastPage
页面大小(即每一页显示的条数):用户传递到后台——count
总条数:通过 SQL 语句查询得来的——totalCount
可以发现页面功能中需要用到的数据有两个是需要通过 SQL 语句查询得来的:一个是页面中显示的数据 List ,另一个是数据的总条数 totalCount,分别对应以下两条 SQL 语句:
SELECT * FROM student LIMIT #{param1}, #{param2}
SELECT COUNT(*) FROM student
通过计算得到的数据有:
总页数:totalPage
总页数 = 总条数 % 页面大小 == 0 ? 总条数 / 页面大小 : 总条数 / 页面大小 + 1
上一页:prePage
上一页 = 当前页 - 1 > = 1 ? 当前页 - 1 : 1
下一页:nextPage
下一页 = 当前页 + 1 <= totalPage ? 当前页 + 1 : totalPage
尾页:lastPage
尾页 = 总条数 % 页面大小 == 0 ? 总条数 - 页面大小 : 总条数 - 总条数 % 页面大小
用户传递的数据:
当前页:currentPage
页面大小:count
二、案例演示:硬分页显示新闻列表
1、创建Web项目PagingNews
2、在web目录里创建META-INF目录,在里面创建数据源配置文件context.xml
<?xml version="1.0" encoding="utf-8" ?>
<Context>
<Resource
name="qing/news" auth="Container"
type="javax.sql.DataSource"
maxAlive="100" maxIdle="30" maxWait="1000"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/qing"
username="root" password="1"
/>
</Context>
3、在src里创建net.qing.news.bean包,在里面创建新闻实体类News
package net.qing.news.bean;
import java.sql.Timestamp;
public class news {
private int nid;
private int ntid;
private String ntname;
private String ntitle;
private String nauthor;
private Timestamp ncreatedate;
private String npicpath;
private String ncontent;
private Timestamp nmodifydate;
private String nsummary;
public int getNid() {
return nid;
}
public void setNid(int nid) {
this.nid = nid;
}
public int getNtid() {
return ntid;
}
public void setNtid(int ntid) {
this.ntid = ntid;
}
public String getNtname() {
return ntname;
}
public void setNtname(String ntname) {
this.ntname = ntname;
}
public String getNtitle() {
return ntitle;
}
public void setNtitle(String ntitle) {
this.ntitle = ntitle;
}
public String getNauthor() {
return nauthor;
}
public void setNauthor(String nauthor) {
this.nauthor = nauthor;
}
public Timestamp getNcreatedate() {
return ncreatedate;
}
public void setNcreatedate(Timestamp ncreatedate) {
this.ncreatedate = ncreatedate;
}
public String getNpicpath() {
return npicpath;
}
public void setNpicpath(String npicpath) {
this.npicpath = npicpath;
}
public String getNcontent() {
return ncontent;
}
public void setNcontent(String ncontent) {
this.ncontent = ncontent;
}
public Timestamp getNmodifydate() {
return nmodifydate;
}
public void setNmodifydate(Timestamp nmodifydate) {
this.nmodifydate = nmodifydate;
}
public String getNsummary() {
return nsummary;
}
public void setNsummary(String nsummary) {
this.nsummary = nsummary;
}
@Override
public String toString() {
return "news{" +
"nid=" + nid +
", ntid=" + ntid +
", ntname='" + ntname + '\'' +
", ntitle='" + ntitle + '\'' +
", nauthor='" + nauthor + '\'' +
", ncreatedate=" + ncreatedate +
", npicpath='" + npicpath + '\'' +
", ncontent='" + ncontent + '\'' +
", nmodifydate=" + nmodifydate +
", nsummary='" + nsummary + '\'' +
'}';
}
}
4、创建net.qing.news.dbutil包,在里面创建数据库连接管理类ConnectionManager
package net.qing.news.dbutil;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 功能:数据库连接管理类
* 作者:卿加波
* 日期:2019年11月14日
*/
public class ConnectionManager {
/**
* 私有化构造方法,拒绝实例化
*/
private ConnectionManager() {
}
/**
* 获取数据库连接静态方法
*
* @return 数据库连接
*/
public static Connection getConnection() {
Connection conn = null;
try {
// 创建初始化上下文
Context ctx = new InitialContext();
// 通过数据库连接池查询获取数据源
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/news");
// 通过数据源获取数据库连接
conn = ds.getConnection();
} catch (NamingException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭数据库连接静态方法
*
* @param conn 数据库连接
*/
public static void closeConn(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
5、创建net.qing.news.dao包,在里面创建新闻数据访问类NewsDao
package net.qing.news.dao;
import net.qing.news.bean.News;
import net.qing.news.dbutil.ConnectionManager;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 功能:新闻数据访问类
* 作者:卿加波
* 日期:2019年11月14日
*/
public class NewsDao {
/**
* 获取全部新闻列表
*
* @return 全部新闻列表
*/
public List<News> findAllNews() {
// 定义新闻列表
List<News> newsList = new ArrayList<>();
// 获取数据库连接
Connection conn = ConnectionManager.getConnection();
// 定义SQL字符串
String strSQL = "select * from news";
try {
// 创建语句对象
Statement stmt = conn.createStatement();
// 执行SQL查询,返回结果集
ResultSet rs = stmt.executeQuery(strSQL);
// 遍历结果集
while (rs.next()) {
// 创建新闻实体对象
News news = new News();
// 将当前记录字段用于设置新闻实体属性
news.setNid(rs.getInt("nid"));
news.setNtid(rs.getInt("ntid"));
news.setNtitle(rs.getString("ntitle"));
news.setNauthor(rs.getString("nauthor"));
news.setNcreatedate(rs.getTimestamp("ncreatedate"));
news.setNpicpath(rs.getString("npicpath"));
news.setNcontent(rs.getString("ncontent"));
news.setNmodifydate(rs.getTimestamp("nmodifydate"));
news.setNsummary(rs.getString("nsummary"));
// 将当前新闻实体添加到新闻列表
newsList.add(news);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭数据库连接
ConnectionManager.closeConn(conn);
}
// 返回新闻列表
return newsList;
}
/**
* 获取总记录数
*
* @return 总记录数
*/
public int getCount() {
// 返回总记录数
return findAllNews().size();
}
/**
* 获取总页数
*
* @param count 总记录数
* @param pageSize 每页记录数
* @return 总页数
*/
public int getTotalPages (int count, int pageSize) {
// 返回总页数
return count % pageSize == 0 ? count / pageSize : count / pageSize + 1;
}
/**
* 按页获取新闻列表
*
* @param currentPage 当前页码
* @param pageSize 每页记录数
* @return 当前页新闻列表
*/
public List<News> findNewsByPage(int currentPage, int pageSize) {
// 定义新闻列表
List<News> newsList = new ArrayList<>();
// 获取数据库连接
Connection conn = ConnectionManager.getConnection();
// 定义SQL字符串
String strSQL = "select * from news limit ?, ?";
try {
// 创建预备语句对象
PreparedStatement pstmt = conn.prepareStatement(strSQL);
// 设置占位符的值
pstmt.setInt(1, pageSize * (currentPage - 1));
pstmt.setInt(2, pageSize);
// 执行SQL查询,返回结果集
ResultSet rs = pstmt.executeQuery();
// 遍历结果集
while (rs.next()) {
// 创建新闻实体对象
News news = new News();
// 将当前记录字段用于设置新闻实体属性
news.setNid(rs.getInt("nid"));
news.setNtid(rs.getInt("ntid"));
news.setNtitle(rs.getString("ntitle"));
news.setNauthor(rs.getString("nauthor"));
news.setNcreatedate(rs.getTimestamp("ncreatedate"));
news.setNpicpath(rs.getString("npicpath"));
news.setNcontent(rs.getString("ncontent"));
news.setNmodifydate(rs.getTimestamp("nmodifydate"));
news.setNsummary(rs.getString("nsummary"));
// 将当前新闻实体添加到新闻列表
newsList.add(news);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭数据库连接
ConnectionManager.closeConn(conn);
}
// 返回新闻列表
return newsList;
}
}
6、在web目录里创建首页文件index.jsp
7、在web目录里创建不分页显示全部新闻页面allnews.jsp
<%@ page import="net.qing.news.dao.NewsDao" %>
<%@ page import="net.qing.news.bean.News" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>不分页显示全部新闻列表</title>
</head>
<body>
<%
// 创建新闻数据访问对象
NewsDao newsDao = new NewsDao();
// 获取全部新闻列表
List<News> newsList = newsDao.findAllNews();
// 在页面上显示数据
out.print("<table border='1' cellpadding='10'>");
out.print("<tr><th>编号</th><th>标题</th><th>创建时间</th></tr>");
// 遍历新闻列表
for (News news: newsList) {
out.print("<tr><td>" + news.getNid() + "</td>");
out.print("<td>" + news.getNtitle() + "</td>");
out.print("<td>" + news.getNcreatedate() + "</td></tr>");
}
out.print("</table>");
%>
</body>
</html>
8、查看效果
9、在web目录里创建分页显示全部新闻列表页面pagingnews.jsp
<%--
Created by IntelliJ IDEA.
User: lenovo
Date: 2019/11/14
Time: 10:36
To change this template use File | Settings | File Templates.
--%>
<%@ page import="net.qing.news.dao.NewsDao" %>
<%@ page import="net.qing.news.bean.News" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>分页显示全部新闻列表</title>
</head>
<body>
<%
// 创建新闻数据访问对象
NewsDao newsDao = new NewsDao();
// 获取总记录数
int count = newsDao.getCount();
// 定义每页记录数
int pageSize = 5;
// 获取总页数
int totalPages = newsDao.getTotalPages(count, pageSize);
// 获取当前页码
String currentPage = request.getParameter("pageIndex");
// 首次进入页面,应该显示第一页内容
if (currentPage == null) {
currentPage = "1";
}
// 获取页索引
int pageIndex = Integer.parseInt(currentPage);
// 对首页与末页的页索引进行控制
if (pageIndex < 1) {
pageIndex = 1;
} else if (pageIndex > totalPages) {
pageIndex = totalPages;
}
// 获取当前页新闻列表
List<News> newsList = newsDao.findNewsByPage(pageIndex, pageSize);
// 在页面上显示数据
out.print("<table border='1' cellpadding='10' style='margin: 0px auto'>");
out.print("<tr><th>编号</th><th>标题</th><th>创建时间</th></tr>");
// 遍历新闻列表
for (News news: newsList) {
out.print("<tr><td>" + news.getNid() + "</td>");
out.print("<td>" + news.getNtitle() + "</td>");
out.print("<td>" + news.getNcreatedate() + "</td></tr>");
}
out.print("</table>");
%>
<%-- 实现分页操作【首页、上一页、下一页、末页】--%>
<p style="text-align: center">
<a href="pagingnews.jsp?pageIndex=1">[首页]</a>
<a href="pagingnews.jsp?pageIndex=<%= pageIndex - 1 %>">[上一页]</a>
<a href="pagingnews.jsp?pageIndex=<%= pageIndex + 1 %>">[下一页]</a>
<a href="pagingnews.jsp?pageIndex=<%= totalPages %>">[末页]</a>
<%= pageIndex %> / <%= totalPages %>
</p>
</body>
</html>
10、重启tomcat服务器,查看分页显示效果
11、软分页例子
<%@ page import="net.qing.news.bean.News" %>
<%@ page import="net.qing.news.dao.NewsDao" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %><%--
Created by IntelliJ IDEA.
User: lenovo
Date: 2019/11/14
Time: 11:35
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>软分页显示新闻列表</title>
</head>
<body>
<%
//获取总页数
NewsDao newsDao = new NewsDao();
int count = newsDao.getCount();
int pageSize = 5;
int totalPages = newsDao.getTotalPages(count, pageSize);
List<News> allnews = newsDao.findAllNews();
// 获取当前页码
String currentPage = request.getParameter("pageIndex");
if (currentPage == null) {
currentPage = "1";
}
int pageIndex = Integer.parseInt(currentPage);
//对首页与末页进行控制
if (pageIndex < 1) {
pageIndex = 1;
} else if (pageIndex > totalPages) {
pageIndex = totalPages;
}
// 生成当前页的新闻列表
List<News> newsList = new ArrayList<>();
for (int i = (pageIndex - 1) * pageSize; i < pageIndex * pageSize; i++) {
if (i < count) {
newsList.add(allnews.get(i));
}
}
out.print("<table border='1' cellpadding='10' style='margin: 0px auto'>");
out.print("<tr><th>编号</th><th>标题</th><th>创建时间</th></tr>");
for (News news : newsList) {
out.print("<tr><td>" + news.getNid() + "</td>");
out.print("<td>" + news.getNtitle() + "</td>");
out.print("<td>" + news.getNcreatedate() + "</td></tr>");
}
out.print("</table>");
%>
<p style="text-align: center">
<a href="softpagingnews.jsp?pageIndex=1">[首页]</a>
<a href="softpagingnews.jsp?pageIndex=<%= pageIndex - 1 %>">[上一页]</a>
<a href="softpagingnews.jsp?pageIndex=<%= pageIndex + 1 %>">[下一页]</a>
<a href="softpagingnews.jsp?pageIndex=<%= totalPages %>">[末页]</a>
<%= pageIndex %> / <%= totalPages %>
</p>
</body>
</html>
12、重启服务,查看效果