原文: http://zetcode.com/articles/javaservletpagination/

Java servlet 分页教程显示了如何使用 Java servlet 进行分页。 在示例中,Bootstrap 用于 UI。

分页

分页是将内容分为几页的过程。 用户具有用于通过特定页面链接访问这些页面的导航界面。 导航通常包括上一个/下一个和第一个/最后一个链接。 当数据库中有大量数据或一页中显示许多项时,将使用分页。

Java Servlet

Servlet 是 Java 类,可响应特定类型的网络请求-最常见的是 HTTP 请求。 Java servlet 用于创建 Web 应用。 它们在 servlet 容器(例如 Tomcat 或 Jetty)中运行。 现代 Java Web 开发使用在 servlet 之上构建的框架。

Bootstrap

Bootstrap 是 Twitter 的一个 UI 库,用于创建响应式,移动优先的 Web 应用。

Java Servlet 分页示例

在以下应用中,我们从 MySQL 数据库加载数据并将其显示在表中。 有一个导航系统可以遍历数据库表中的所有数据。 在将数据显示在表中之前,用户可以选择表将显示多少行。

除了从数据库表中获取数据之外,我们还需要知道数据库表中所有行的数量,每页的记录数以及要在导航中显示的页面数。 SQL 语句可以计算出数据库中所有行的数量。 用户以 HTML 格式选择每页的记录数。 最后,从其他两个值计算分页中的页数。

countries_mysql.sql

  1. CREATE TABLE Countries(ID BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  2. Name VARCHAR(100), Population INT);
  3. INSERT INTO Countries(Name, Population) VALUES('China', 1382050000);
  4. INSERT INTO Countries(Name, Population) VALUES('India', 1313210000);
  5. INSERT INTO Countries(Name, Population) VALUES('USA', 324666000);
  6. INSERT INTO Countries(Name, Population) VALUES('Indonesia', 260581000);
  7. INSERT INTO Countries(Name, Population) VALUES('Brazil', 207221000);
  8. INSERT INTO Countries(Name, Population) VALUES('Pakistan', 196626000);
  9. INSERT INTO Countries(Name, Population) VALUES('Nigeria', 186988000);
  10. INSERT INTO Countries(Name, Population) VALUES('Bangladesh', 162099000);
  11. INSERT INTO Countries(Name, Population) VALUES('Nigeria', 186988000);
  12. INSERT INTO Countries(Name, Population) VALUES('Russia', 146838000);
  13. INSERT INTO Countries(Name, Population) VALUES('Japan', 126830000);
  14. INSERT INTO Countries(Name, Population) VALUES('Mexico', 122273000);
  15. INSERT INTO Countries(Name, Population) VALUES('Philippines', 103738000);
  16. INSERT INTO Countries(Name, Population) VALUES('Ethiopia', 101853000);
  17. INSERT INTO Countries(Name, Population) VALUES('Vietnam', 92700000);
  18. INSERT INTO Countries(Name, Population) VALUES('Egypt', 92641000);
  19. INSERT INTO Countries(Name, Population) VALUES('Germany', 82800000);
  20. INSERT INTO Countries(Name, Population) VALUES('the Congo', 82243000);
  21. INSERT INTO Countries(Name, Population) VALUES('Iran', 82800000);
  22. INSERT INTO Countries(Name, Population) VALUES('Turkey', 79814000);
  23. INSERT INTO Countries(Name, Population) VALUES('Thailand', 68147000);
  24. INSERT INTO Countries(Name, Population) VALUES('France', 66984000);
  25. INSERT INTO Countries(Name, Population) VALUES('United Kingdom', 60589000);
  26. INSERT INTO Countries(Name, Population) VALUES('South Africa', 55908000);
  27. INSERT INTO Countries(Name, Population) VALUES('Myanmar', 51446000);
  28. INSERT INTO Countries(Name, Population) VALUES('South Korea', 68147000);
  29. INSERT INTO Countries(Name, Population) VALUES('Colombia', 49129000);
  30. INSERT INTO Countries(Name, Population) VALUES('Kenya', 47251000);
  31. INSERT INTO Countries(Name, Population) VALUES('Spain', 46812000);
  32. INSERT INTO Countries(Name, Population) VALUES('Argentina', 43850000);
  33. INSERT INTO Countries(Name, Population) VALUES('Ukraine', 42603000);
  34. INSERT INTO Countries(Name, Population) VALUES('Sudan', 41176000);
  35. INSERT INTO Countries(Name, Population) VALUES('Algeria', 40400000);
  36. INSERT INTO Countries(Name, Population) VALUES('Poland', 38439000);

该 SQL 脚本在 MySQL 中创建Countries表。

  1. $ tree
  2. .
  3. ├── nb-configuration.xml
  4. ├── pom.xml
  5. └── src
  6. ├── main
  7. ├── java
  8. └── com
  9. └── zetcode
  10. ├── bean
  11. └── Country.java
  12. ├── service
  13. ├── CountryService.java
  14. └── ICountryService.java
  15. └── web
  16. └── ReadCountries.java
  17. ├── resources
  18. └── webapp
  19. ├── index.html
  20. ├── listCountries.jsp
  21. ├── META-INF
  22. └── context.xml
  23. └── WEB-INF
  24. └── test
  25. └── java

这是项目结构。

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  5. http://maven.apache.org/xsd/maven-4.0.0.xsd">
  6. <modelVersion>4.0.0</modelVersion>
  7. <groupId>com.zetcode</groupId>
  8. <artifactId>JavaServletPagination</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <packaging>war</packaging>
  11. <name>JavaServletPagination</name>
  12. <properties>
  13. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  14. <maven.compiler.source>1.8</maven.compiler.source>
  15. <maven.compiler.target>1.8</maven.compiler.target>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>javax.servlet</groupId>
  20. <artifactId>javax.servlet-api</artifactId>
  21. <version>3.1.0</version>
  22. <scope>provided</scope>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.springframework</groupId>
  26. <artifactId>spring-jdbc</artifactId>
  27. <version>5.0.2.RELEASE</version>
  28. </dependency>
  29. <dependency>
  30. <groupId>mysql</groupId>
  31. <artifactId>mysql-connector-java</artifactId>
  32. <version>5.1.45</version>
  33. </dependency>
  34. <dependency>
  35. <groupId>javax.servlet</groupId>
  36. <artifactId>jstl</artifactId>
  37. <version>1.2</version>
  38. </dependency>
  39. </dependencies>
  40. <build>
  41. <plugins>
  42. <plugin>
  43. <groupId>org.apache.maven.plugins</groupId>
  44. <artifactId>maven-war-plugin</artifactId>
  45. <version>2.3</version>
  46. <configuration>
  47. <failOnMissingWebXml>false</failOnMissingWebXml>
  48. </configuration>
  49. </plugin>
  50. </plugins>
  51. </build>
  52. </project>

这是 Maven POM 文件。 javax.servlet-api工件用于 servlet。 spring-jdbc依赖项用于JdbcTemplate库,该库简化了 Java 中的数据库编程。 mysql-connector-java是 Java 语言的 MySQL 驱动程序。 jstl依赖项为 JSP 页面提供了一些附加功能。 maven-war-plugin负责收集 Web 应用的所有工件依赖项,类和资源,并将它们打包到 Web 应用存档(WAR)中。

context.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <Context path="/JavaServletPagination"/>

在 Tomcat context.xml文件中,我们定义了上下文路径。 它是 Web 应用的名称。

Country.java

  1. package com.zetcode.bean;
  2. public class Country {
  3. private String name;
  4. private int population;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getPopulation() {
  12. return population;
  13. }
  14. public void setPopulation(int population) {
  15. this.population = population;
  16. }
  17. }

Country bean 从Countries数据库表中保留一行。

ReadCountries.java

  1. package com.zetcode.web;
  2. import com.zetcode.bean.Country;
  3. import com.zetcode.service.CountryService;
  4. import java.io.IOException;
  5. import java.util.List;
  6. import javax.servlet.RequestDispatcher;
  7. import javax.servlet.ServletException;
  8. import javax.servlet.annotation.WebServlet;
  9. import javax.servlet.http.HttpServlet;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. @WebServlet(name = "ReadCountries", urlPatterns = {"/ReadCountries"})
  13. public class ReadCountries extends HttpServlet {
  14. @Override
  15. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  16. throws ServletException, IOException {
  17. response.setContentType("text/html;charset=UTF-8");
  18. int currentPage = Integer.valueOf(request.getParameter("currentPage"));
  19. int recordsPerPage = Integer.valueOf(request.getParameter("recordsPerPage"));
  20. CountryService countryService = new CountryService();
  21. List<Country> countries = countryService.findCountries(currentPage,
  22. recordsPerPage);
  23. request.setAttribute("countries", countries);
  24. int rows = countryService.getNumberOfRows();
  25. int nOfPages = rows / recordsPerPage;
  26. if (nOfPages % recordsPerPage > 0) {
  27. nOfPages++;
  28. }
  29. request.setAttribute("noOfPages", nOfPages);
  30. request.setAttribute("currentPage", currentPage);
  31. request.setAttribute("recordsPerPage", recordsPerPage);
  32. RequestDispatcher dispatcher = request.getRequestDispatcher("listCountries.jsp");
  33. dispatcher.forward(request, response);
  34. }
  35. }

ReadCountries Servlet 确定将从请求属性中检索多少数据,并从数据库表中读取指定的行数。

  1. @WebServlet(name = "ReadCountries", urlPatterns = {"/ReadCountries"})

Java 类用@WebServlet注解修饰。 它映射到ReadCountries URL 模式。

  1. response.setContentType("text/html;charset=UTF-8");

Servlet 将以 HTML 输出数据,并且数据的编码设置为 UTF-8。

  1. int currentPage = Integer.valueOf(request.getParameter("currentPage"));
  2. int recordsPerPage = Integer.valueOf(request.getParameter("recordsPerPage"));

从请求中我们得到两个重要的值:当前页和每页的记录数。

  1. CountryService countryService = new CountryService();
  2. List<Country> countries = countryService.findCountries(currentPage,
  3. recordsPerPage);
  4. request.setAttribute("countries", countries);

CountryService是用于连接到数据库并读取数据的服务类。 检索国家列表并将其设置为请求的属性。 稍后将由目标 JSP 页面使用。

  1. int rows = countryService.getNumberOfRows();
  2. int nOfPages = rows / recordsPerPage;
  3. if (nOfPages % recordsPerPage > 0) {
  4. nOfPages++;
  5. }

我们使用getNumberOfRows()服务方法从数据库表中获取所有行的数目。 我们计算导航中的页面数。

  1. request.setAttribute("noOfPages", nOfPages);
  2. request.setAttribute("currentPage", currentPage);
  3. request.setAttribute("recordsPerPage", recordsPerPage);

页数,当前页和每页的记录数是我们建立分页所需的值。

  1. RequestDispatcher dispatcher = request.getRequestDispatcher("listCountries.jsp");
  2. dispatcher.forward(request, response);

处理被转发到listCountries.jsp页面。

ICountryService.java

  1. package com.zetcode.service;
  2. import com.zetcode.bean.Country;
  3. import java.util.List;
  4. public interface ICountryService {
  5. public List<Country> findCountries(int currentPage, int numOfRecords);
  6. public int getNumberOfRows();
  7. }

ICountryService包含两种签约方法:findCountries()getNumberOfRows()

CountryService.java

  1. package com.zetcode.service;
  2. import com.zetcode.bean.Country;
  3. import java.sql.SQLException;
  4. import java.util.List;
  5. import java.util.logging.Level;
  6. import java.util.logging.Logger;
  7. import org.springframework.jdbc.core.BeanPropertyRowMapper;
  8. import org.springframework.jdbc.core.JdbcTemplate;
  9. import org.springframework.jdbc.datasource.SimpleDriverDataSource;
  10. public class CountryService implements ICountryService {
  11. @Override
  12. public List<Country> findCountries(int currentPage, int recordsPerPage) {
  13. List<Country> countries = null;
  14. int start = currentPage * recordsPerPage - recordsPerPage;
  15. try {
  16. String sql = "SELECT * FROM Countries LIMIT ?, ?";
  17. SimpleDriverDataSource ds = new SimpleDriverDataSource();
  18. ds.setDriver(new com.mysql.jdbc.Driver());
  19. ds.setUrl("jdbc:mysql://localhost:3306/testdb");
  20. ds.setUsername("testuser");
  21. ds.setPassword("test623");
  22. JdbcTemplate jtm = new JdbcTemplate(ds);
  23. countries = jtm.query(sql, new Object[] {start, recordsPerPage},
  24. new BeanPropertyRowMapper(Country.class));
  25. } catch (SQLException ex) {
  26. Logger.getLogger(CountryService.class.getName()).log(Level.SEVERE,
  27. null, ex);
  28. }
  29. return countries;
  30. }
  31. @Override
  32. public int getNumberOfRows() {
  33. int numOfRows = 0;
  34. try {
  35. String sql = "SELECT COUNT(Id) FROM Countries";
  36. SimpleDriverDataSource ds = new SimpleDriverDataSource();
  37. ds.setDriver(new com.mysql.jdbc.Driver());
  38. ds.setUrl("jdbc:mysql://localhost:3306/testdb");
  39. ds.setUsername("testuser");
  40. ds.setPassword("test623");
  41. JdbcTemplate jtm = new JdbcTemplate(ds);
  42. numOfRows = jtm.queryForObject(sql, Integer.class);
  43. } catch (SQLException ex) {
  44. Logger.getLogger(CountryService.class.getName()).log(Level.SEVERE,
  45. null, ex);
  46. }
  47. return numOfRows;
  48. }
  49. }

CountryService包含两种合同方法的实现。

  1. String sql = "SELECT * FROM Countries LIMIT ?, ?";

SQL LIMIT子句用于获取当前页面的行数。

  1. JdbcTemplate jtm = new JdbcTemplate(ds);
  2. countries = jtm.query(sql, new Object[] {start, recordsPerPage},
  3. new BeanPropertyRowMapper(Country.class));

JdbcTemplate用于执行 SQL 语句。 在BeanPropertyRowMapper的帮助下,行自动映射到Country bean。

  1. String sql = "SELECT COUNT(Id) FROM Countries";

通过此 SQL 语句,我们从数据库表中获取行数。

index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Home page</title>
  5. <meta charset="UTF-8">
  6. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
  7. </head>
  8. <body class="m-3">
  9. <h1>Show countries</h1>
  10. <form action="ReadCountries">
  11. <input type="hidden" name="currentPage" value="1">
  12. <div class="form-group col-md-4">
  13. <label for="records">Select records per page:</label>
  14. <select class="form-control" id="records" name="recordsPerPage">
  15. <option value="5">5</option>
  16. <option value="10" selected>10</option>
  17. <option value="15">15</option>
  18. </select>
  19. </div>
  20. <button type="submit" class="btn btn-primary">Submit</button>
  21. </form>
  22. <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" ></script>
  23. <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" ></script>
  24. <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" ></script>
  25. </body>
  26. </html>

这是主页。 它包含一个 HTML 表单,用于通过select标签选择每页的记录数。 该表单使用 Bootstrap 库中的样式类。 提交表单后,处理将发送到ReadCountries Servlet。

  1. <input type="hidden" name="currentPage" value="1">

该表单包含一个隐藏的input标记,该标记将currentPage参数设置为 1。

  1. <select class="form-control" id="records" name="recordsPerPage">
  2. <option value="5">5</option>
  3. <option value="10" selected>10</option>
  4. <option value="15">15</option>
  5. </select>

select标签允许每页选择 5、10 或 15 条记录。

  1. <button type="submit" class="btn btn-primary">Submit</button>

提交按钮执行表单。

listCountries.jsp

  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>
  2. <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  3. <!DOCTYPE html>
  4. <html>
  5. <head>
  6. <meta charset="UTF-8">
  7. <title>Countries</title>
  8. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
  9. </head>
  10. <body class="m-3">
  11. <div class="row col-md-6">
  12. <table class="table table-striped table-bordered table-sm">
  13. <tr>
  14. <th>Name</th>
  15. <th>Population</th>
  16. </tr>
  17. <c:forEach items="${countries}" var="country">
  18. <tr>
  19. <td>${country.getName()}</td>
  20. <td>${country.getPopulation()}</td>
  21. </tr>
  22. </c:forEach>
  23. </table>
  24. </div>
  25. <nav aria-label="Navigation for countries">
  26. <ul class="pagination">
  27. <c:if test="${currentPage != 1}">
  28. <li class="page-item"><a class="page-link"
  29. href="ReadCountries?recordsPerPage=${recordsPerPage}&currentPage=${currentPage-1}">Previous</a>
  30. </li>
  31. </c:if>
  32. <c:forEach begin="1" end="${noOfPages}" var="i">
  33. <c:choose>
  34. <c:when test="${currentPage eq i}">
  35. <li class="page-item active"><a class="page-link">
  36. ${i} <span class="sr-only">(current)</span></a>
  37. </li>
  38. </c:when>
  39. <c:otherwise>
  40. <li class="page-item"><a class="page-link"
  41. href="ReadCountries?recordsPerPage=${recordsPerPage}&currentPage=${i}">${i}</a>
  42. </li>
  43. </c:otherwise>
  44. </c:choose>
  45. </c:forEach>
  46. <c:if test="${currentPage lt noOfPages}">
  47. <li class="page-item"><a class="page-link"
  48. href="ReadCountries?recordsPerPage=${recordsPerPage}&currentPage=${currentPage+1}">Next</a>
  49. </li>
  50. </c:if>
  51. </ul>
  52. <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
  53. <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
  54. <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
  55. </body>
  56. </html>

listCountries.jsp在表格和分页系统中显示数据。 Bootstrap 用于使 UI 响应并看起来不错。

  1. <table class="table table-striped table-bordered table-sm">

tabletable-stripedtable-borderedtable-sm都是 Bootstrap 类。

  1. <c:forEach items="${countries}" var="country">
  2. <tr>
  3. <td>${country.getName()}</td>
  4. <td>${country.getPopulation()}</td>
  5. </tr>
  6. </c:forEach>

使用 JSTL 的forEach标签,我们可以显示当前页面的所有数据。

  1. <c:if test="${currentPage != 1}">
  2. <li class="page-item"><a class="page-link"
  3. href="ReadCountries?recordsPerPage=${recordsPerPage}&currentPage=${currentPage-1}">Previous</a>
  4. </li>
  5. </c:if>

使用c:if标签,我们仅在存在前一个链接时显示它。 在链接中,我们将recordsPerPagecurrentPage值传递给请求对象。

  1. <c:forEach begin="1" end="${noOfPages}" var="i">
  2. <c:choose>
  3. <c:when test="${currentPage eq i}">
  4. <li class="page-item active"><a class="page-link">
  5. ${i} <span class="sr-only">(current)</span></a>
  6. </li>
  7. </c:when>
  8. <c:otherwise>
  9. <li class="page-item"><a class="page-link"
  10. href="ReadCountries?recordsPerPage=${recordsPerPage}&currentPage=${i}">${i}</a>
  11. </li>
  12. </c:otherwise>
  13. </c:choose>
  14. </c:forEach>

使用forEach标签,我们显示所有页面链接。

Java Servlet 分页 - 图1

图:Java Servlet 分页

该示例显示了一个装有数据和分页系统的表。 当前选择的页面突出显示。

在本教程中,我们展示了如何使用 Java Servlet 在 Web 应用中创建分页系统。

您可能也对以下相关教程感兴趣: Java Servlet 上传文件Java Log4j 教程Java Servlet RESTful 客户端Java RequestDispatcher从 Java servlet 提供纯文本Java servlet 图像教程Java 教程