Java servlet 分页教程展示了如何使用 Java servlet 进行分页。在示例中,Bootstrap 用于 UI。
分页
分页是将内容分成多个页面的过程。用户有一个导航界面,用于通过特定页面链接访问这些页面。导航通常包括上一个/下一个和第一个/最后一个链接。当数据库中有大量数据或一页中有很多评论要显示时,使用分页。
Java Servlet程序
Servlet是一个 Java 类,它响应特定类型的网络请求——最常见的是 HTTP 请求。Java servlet 用于创建 Web 应用程序。它们在 Tomcat 或 Jetty 等 servlet 容器中运行。现代 Java Web 开发使用构建在 servlet 之上的框架。
Bootstrap
Bootstrap 是来自 Twitter 的 UI 库,用于创建响应式、移动优先的 Web 应用程序。
Java Servlet 分页示例
在以下 Web 应用程序中,我们从 MySQL 数据库加载数据并将其显示在表格中。有一个导航系统可以检查数据库表中的所有数据。在数据显示在表格中之前,用户可以选择表格将显示多少行。Web 应用程序部署在 Jetty 服务器上。
除了从数据库表中获取数据外,我们还需要知道数据库表中的所有行数、每页的记录数以及要在导航中显示的页数。数据库中所有行的数量由一条 SQL 语句计算出来。每页的记录数由用户在 HTML 表单中选择。最后,根据其他两个值计算分页中的页数。
CREATE TABLE countries(id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255), population INT);
INSERT INTO countries(name, population) VALUES('China', 1382050000);
INSERT INTO countries(name, population) VALUES('India', 1313210000);
INSERT INTO countries(name, population) VALUES('USA', 324666000);
INSERT INTO countries(name, population) VALUES('Indonesia', 260581000);
INSERT INTO countries(name, population) VALUES('Brazil', 207221000);
INSERT INTO countries(name, population) VALUES('Pakistan', 196626000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Bangladesh', 162099000);
INSERT INTO countries(name, population) VALUES('Nigeria', 186988000);
INSERT INTO countries(name, population) VALUES('Russia', 146838000);
INSERT INTO countries(name, population) VALUES('Japan', 126830000);
INSERT INTO countries(name, population) VALUES('Mexico', 122273000);
INSERT INTO countries(name, population) VALUES('Philippines', 103738000);
INSERT INTO countries(name, population) VALUES('Ethiopia', 101853000);
INSERT INTO countries(name, population) VALUES('Vietnam', 92700000);
INSERT INTO countries(name, population) VALUES('Egypt', 92641000);
INSERT INTO countries(name, population) VALUES('Germany', 82800000);
INSERT INTO countries(name, population) VALUES('the Congo', 82243000);
INSERT INTO countries(name, population) VALUES('Iran', 82800000);
INSERT INTO countries(name, population) VALUES('Turkey', 79814000);
INSERT INTO countries(name, population) VALUES('Thailand', 68147000);
INSERT INTO countries(name, population) VALUES('France', 66984000);
INSERT INTO countries(name, population) VALUES('United Kingdom', 60589000);
INSERT INTO countries(name, population) VALUES('South Africa', 55908000);
INSERT INTO countries(name, population) VALUES('Myanmar', 51446000);
INSERT INTO countries(name, population) VALUES('South Korea', 68147000);
INSERT INTO countries(name, population) VALUES('Colombia', 49129000);
INSERT INTO countries(name, population) VALUES('Kenya', 47251000);
INSERT INTO countries(name, population) VALUES('Spain', 46812000);
INSERT INTO countries(name, population) VALUES('Argentina', 43850000);
INSERT INTO countries(name, population) VALUES('Ukraine', 42603000);
INSERT INTO countries(name, population) VALUES('Sudan', 41176000);
INSERT INTO countries(name, population) VALUES('Algeria', 40400000);
INSERT INTO countries(name, population) VALUES('Poland', 38439000);
此 SQL 脚本countries
在 MySQL 中创建表。
pom.xml
src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ ├── model
│ │ │ └── Country.java
│ │ ├── service
│ │ │ ├── CountryService.java
│ │ │ └── ICountryService.java
│ │ └── web
│ │ └── ReadCountries.java
│ ├── resources
│ └── webapp
│ ├── index.html
│ └── listCountries.jsp
└── test
└── java
这是项目结构。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zetcode</groupId>
<artifactId>PaginationEx</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.30.v20200611</version>
</plugin>
</plugins>
</build>
</project>
这是 Maven POM 文件。该javax.servlet-api
工件用于 servlet。该spring-jdbc
依赖项用于 JdbcTemplate 库,它简化了 Java 中的数据库编程。这mysql-connector-java
是Java语言的MySQL驱动程序。该jstl
依赖项为 JSP 页面提供了一些附加功能。
maven-war-plugin
负责收集 Web 应用程序的所有工件依赖项、类和资源,并将它们打包到 Web 应用程序存档 (WAR) 中 。这jetty-maven-plugin
是一个有用的 Maven 插件,用于快速开发和测试。它创建一个 Web 应用程序,启动一个 Jetty Web 服务器,并在服务器上部署该应用程序。
package com.zetcode.model;
import java.util.Objects;
public class Country {
private String name;
private int population;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Country country = (Country) o;
return population == country.population
&& Objects.equals(name, country.name);
}
@Override
public int hashCode() {
return Objects.hash(name, population);
}
}
bean 保存数据库表中 的Country
一行 。countries
package com.zetcode.web;
import com.zetcode.model.Country;
import com.zetcode.service.CountryService;
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "ReadCountries", urlPatterns = {"/ReadCountries"})
public class ReadCountries extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
int currentPage = Integer.parseInt(request.getParameter("currentPage"));
int recordsPerPage = Integer.parseInt(request.getParameter("recordsPerPage"));
CountryService countryService = new CountryService();
List<Country> countries = countryService.findCountries(currentPage,
recordsPerPage);
request.setAttribute("countries", countries);
int rows = countryService.getNumberOfRows();
int nOfPages = rows / recordsPerPage;
if (nOfPages % recordsPerPage > 0) {
nOfPages++;
}
request.setAttribute("noOfPages", nOfPages);
request.setAttribute("currentPage", currentPage);
request.setAttribute("recordsPerPage", recordsPerPage);
RequestDispatcher dispatcher = request.getRequestDispatcher("listCountries.jsp");
dispatcher.forward(request, response);
}
}
ReadCountries
servlet 确定将从请求属性中检索多少数据并从数据库表中读取指定数量的行 。
@WebServlet(name = "ReadCountries", urlPatterns = {"/ReadCountries"})
Java 类是用@WebServlet
注解修饰的。它映射到ReadCountries
URL 模式。
response.setContentType("text/html;charset=UTF-8");
servlet 将以 HTML 格式输出数据,并且数据的编码设置为 UTF-8。
int currentPage = Integer.parseInt(request.getParameter("currentPage"));
int recordsPerPage = Integer.parseInt(request.getParameter("recordsPerPage"));
从请求中我们得到两个重要的值:当前页面和每页的记录数。(跳过对值的验证。)
var countryService = new CountryService();
List<Country> countries = countryService.findCountries(currentPage,
recordsPerPage);
request.setAttribute("countries", countries);
CountryService
是用于连接数据库和读取数据的服务类。检索国家列表并将其设置为请求的属性。稍后将由目标 JSP 页面使用。
int rows = countryService.getNumberOfRows();
int nOfPages = rows / recordsPerPage;
if (nOfPages % recordsPerPage > 0) {
nOfPages++;
}
getNumberOfRows
我们使用service 方法 从数据库表中获取所有行的数量 。我们计算导航中的页面数。
request.setAttribute("noOfPages", nOfPages);
request.setAttribute("currentPage", currentPage);
request.setAttribute("recordsPerPage", recordsPerPage);
页数、当前页数和每页记录数是我们构建分页所需的值。
var dispatcher = request.getRequestDispatcher("listCountries.jsp");
dispatcher.forward(request, response);
处理被转发到listCountries.jsp
页面。
package com.zetcode.service;
import com.zetcode.model.Country;
import java.util.List;
public interface ICountryService {
List<Country> findCountries(int currentPage, int numOfRecords);
Integer getNumberOfRows();
}
ICountryService
包含两种合约方法 :findCountries
和getNumberOfRows
.
package com.zetcode.service;
import com.zetcode.model.Country;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
public class CountryService implements ICountryService {
@Override
public List<Country> findCountries(int currentPage, int recordsPerPage) {
List<Country> countries = null;
int start = currentPage * recordsPerPage - recordsPerPage;
try {
String sql = "SELECT * FROM countries LIMIT ?, ?";
SimpleDriverDataSource ds = new SimpleDriverDataSource();
ds.setDriver(new com.mysql.jdbc.Driver());
ds.setUrl("jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC");
ds.setUsername("root");
ds.setPassword("root");
JdbcTemplate jtm = new JdbcTemplate(ds);
countries = jtm.query(sql, new Object[]{start, recordsPerPage},
new BeanPropertyRowMapper<>(Country.class));
} catch (SQLException ex) {
Logger.getLogger(CountryService.class.getName()).log(Level.SEVERE,
null, ex);
}
return countries;
}
@Override
public Integer getNumberOfRows() {
Integer numOfRows = 0;
try {
String sql = "SELECT COUNT(id) FROM countries";
SimpleDriverDataSource ds = new SimpleDriverDataSource();
ds.setDriver(new com.mysql.jdbc.Driver());
ds.setUrl("jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC");
ds.setUsername("root");
ds.setPassword("root");
JdbcTemplate jtm = new JdbcTemplate(ds);
numOfRows = jtm.queryForObject(sql, Integer.class);
} catch (SQLException ex) {
Logger.getLogger(CountryService.class.getName()).log(Level.SEVERE,
null, ex);
}
return numOfRows;
}
}
包含两种合约方法的CountryService
实现。
String sql = "SELECT * FROM countries LIMIT ?, ?";
SQL LIMIT 子句用于获取当前页面的行数。
var jtm = new JdbcTemplate(ds);
countries = jtm.query(sql, new Object[] {start, recordsPerPage},
new BeanPropertyRowMapper(Country.class));
JdbcTemplate
用于执行 SQL 语句。在. Country
_BeanPropertyRowMapper
String sql = "SELECT COUNT(id) FROM countries";
使用这个 SQL 语句,我们从数据库表中获取行数。
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
crossorigin="anonymous">
</head>
<body>
<main class="m-3">
<h1>Show countries</h1>
<form action="ReadCountries">
<input type="hidden" name="currentPage" value="1">
<div class="form-group col-md-4">
<label for="records">Select records per page:</label>
<select class="form-control" id="records" name="recordsPerPage">
<option value="5">5</option>
<option value="10" selected>10</option>
<option value="15">15</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</main>
</body>
</html>
这是主页。select
它包含一个 HTML 表单,用于选择带有标签的每页记录数。该表单使用来自 Bootstrap 库的样式类。提交表单后,处理被发送到ReadCountries
servlet。
<input type="hidden" name="currentPage" value="1">
该表单包含一个隐藏input
标签,该标签将currentPage
参数设置为 1。
<select class="form-control" id="records" name="recordsPerPage">
<option value="5">5</option>
<option value="10" selected>10</option>
<option value="15">15</option>
</select>
select
标签允许每页选择 5、10 或 15 条记录 。
<button type="submit" class="btn btn-primary">Submit</button>
提交按钮执行表单。
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Countries</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
crossorigin="anonymous">
</head>
<body>
<main class="m-3">
<div class="row col-md-6">
<table class="table table-striped table-bordered table-sm">
<tr>
<th>Name</th>
<th>Population</th>
</tr>
<c:forEach items="${countries}" var="country">
<tr>
<td>${country.getName()}</td>
<td>${country.getPopulation()}</td>
</tr>
</c:forEach>
</table>
</div>
<nav aria-label="Navigation for countries">
<ul class="pagination">
<c:if test="${currentPage != 1}">
<li class="page-item"><a class="page-link"
href="ReadCountries?recordsPerPage=${recordsPerPage}¤tPage=${currentPage-1}">Previous</a>
</li>
</c:if>
<c:forEach begin="1" end="${noOfPages}" var="i">
<c:choose>
<c:when test="${currentPage eq i}">
<li class="page-item active"><a class="page-link">
${i} <span class="sr-only">(current)</span></a>
</li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link"
href="ReadCountries?recordsPerPage=${recordsPerPage}¤tPage=${i}">${i}</a>
</li>
</c:otherwise>
</c:choose>
</c:forEach>
<c:if test="${currentPage lt noOfPages}">
<li class="page-item"><a class="page-link"
href="ReadCountries?recordsPerPage=${recordsPerPage}¤tPage=${currentPage+1}">Next</a>
</li>
</c:if>
</ul>
</nav>
</main>
</body>
</html>
显示表格中的listCountries.jsp
数据和分页系统。Bootstrap 用于使 UI 响应良好并看起来不错。
<table class="table table-striped table-bordered table-sm">
, table
, table-striped
,都是 Bootstrap 类 table-bordered
。 table-sm
<c:forEach items="${countries}" var="country">
<tr>
<td>${country.getName()}</td>
<td>${country.getPopulation()}</td>
</tr>
</c:forEach>
使用 JSTL 的forEach
标签,我们显示当前页面的所有数据。
<c:if test="${currentPage != 1}">
<li class="page-item"><a class="page-link"
href="ReadCountries?recordsPerPage=${recordsPerPage}¤tPage=${currentPage-1}">Previous</a>
</li>
</c:if>
使用c:if
标签,我们只在有一个时显示上一个链接。在链接中,我们将recordsPerPage
和currentPage
值传递给请求对象。
<c:forEach begin="1" end="${noOfPages}" var="i">
<c:choose>
<c:when test="${currentPage eq i}">
<li class="page-item active"><a class="page-link">
${i} <span class="sr-only">(current)</span></a>
</li>
</c:when>
<c:otherwise>
<li class="page-item"><a class="page-link"
href="ReadCountries?recordsPerPage=${recordsPerPage}¤tPage=${i}">${i}</a>
</li>
</c:otherwise>
</c:choose>
</c:forEach>
使用forEach
标签,我们显示所有页面链接。
$ mvn jetty:run
我们运行 Jetty 服务器并导航到localhost:8080
.
图:Java Servlet 分页
该示例显示了一个填充有数据的表格和分页系统。当前选择的页面被突出显示。
在本教程中,我们展示了如何使用 Java Servlet 在 Web 应用程序中创建分页系统。