分頁查詢是 Web 應用高頻需求。本文基於 Servlet + JDBC + MySQL,完整實現「後端分頁邏輯 + 前端分頁控件」兩大核心模塊,重點解析 MySQL LIMIT 子句分頁原理、邊界條件處理、性能優化要點,提供可直接落地的生產級代碼範本。
package com.example.web;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class PaginationServlet extends HttpServlet {
// 每頁顯示記錄數
private static final int PAGE_SIZE = 3;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
// 1. 獲取當前頁碼 (默認第 1 頁)
int pageNow = 1;
String sPageNow = req.getParameter("pageNow");
if (sPageNow != null && !sPageNow.trim().isEmpty()) {
try {
pageNow = Integer.parseInt(sPageNow);
if (pageNow < 1) pageNow = 1;
} catch (NumberFormatException e) {
pageNow = 1;
}
}
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 2. 加載驅動 & 獲取連接 (生產環境請使用連接池)
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3307/spdb?useSSL=false&characterEncoding=UTF-8";
String user = "root";
String password = "root";
conn = DriverManager.getConnection(url, user, password);
// 3. 查詢總記錄數
ps = conn.prepareStatement("SELECT COUNT(*) FROM users");
rs = ps.executeQuery();
int rowCount = 0;
if (rs.next()) {
rowCount = rs.getInt(1);
}
// 4. 計算總頁數
int pageCount = (rowCount + PAGE_SIZE - 1) / PAGE_SIZE; // 向上取整
if (pageCount == 0) pageCount = 1; // 至少 1 頁
// 5. 邊界修正:頁碼不超過總頁數
if (pageNow > pageCount) pageNow = pageCount;
// 6. 分頁查詢核心:LIMIT offset, pageSize
int offset = (pageNow - 1) * PAGE_SIZE;
String sql = "SELECT id, name, passwd, mail, grade FROM users LIMIT ?, ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, offset);
ps.setInt(2, PAGE_SIZE);
rs = ps.executeQuery();
// 7. 輸出數據表格
out.println("| ID | 姓名 | 密碼 | 郵箱 | 等級 |
|---|---|---|---|---|
| " + rs.getInt("id") + " | "); out.println("" + rs.getString("name") + " | "); out.println("" + rs.getString("passwd") + " | "); out.println("" + rs.getString("mail") + " | "); out.println("" + rs.getInt("grade") + " | "); out.println("
系統錯誤: " + e.getMessage() + "
"); } finally { // 9. 資源關閉 (逆序) try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (ps != null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }-- 形式一:LIMIT offset, row_count (MySQL 專有)
SELECT * FROM users LIMIT 0, 3; -- 第 1 頁,每頁 3 條
SELECT * FROM users LIMIT 3, 3; -- 第 2 頁
SELECT * FROM users LIMIT 6, 3; -- 第 3 頁
-- 形式二:LIMIT row_count OFFSET offset (SQL:2008 標準)
SELECT * FROM users LIMIT 3 OFFSET 0;
SELECT * FROM users LIMIT 3 OFFSET 3;
| 參數 | 含義 | 取值範圍 | 說明 |
|---|---|---|---|
| offset | 跳過的行數 (起始偏移量) | ≥ 0 | 第 1 頁 offset=0,第 N 頁 offset=(N-1)*pageSize |
| row_count / pageSize | 返回的最大行數 | >= 0 | 實際返回行數可能少於此值 (最後一頁) |
| 維度 | 建議方案 |
|---|---|
| 連接管理 | 必須使用連接池 (HikariCP/Druid),禁止 Servlet 中直接 DriverManager.getConnection() |
| SQL 注入防護 | 全程使用 PreparedStatement 參數綁定,拼接 SQL 僅限 LIMIT 參數化 |
| 字符編碼 | URL/DB/頁面統一 UTF-8,JDBC URL 添加 characterEncoding=UTF-8 |
| 異常處理 | 捕獲 SQLException、NumberFormatException,返回友好錯誤頁,記錄詳細日志 |
| 性能監控 | 慢查詢日志分析 LIMIT 大偏移、EXPLAIN 執行計劃、索引優化 |
| 緩存總數 | 高頻查詢緩存 rowCount (Redis/Guava Cache),減少 COUNT(*) 全表掃描 |
| 前端組件化 | 提取分頁邏輯為 JSP Tag / Thymeleaf Fragment / Vue/React 組件,複用渲染 |

分頁查詢看似簡單,實則包含「總數統計、邊界計算、LIMIT 語法、導航渲染、性能優化」五大工程課題。掌握 MySQL LIMIT offset, pageSize 零基偏移語義、大偏移量性能陷阱與優化方案、前端頁碼窗口自適應算法,並落地連接池、參數化查詢、編碼統一、異常處理等生產規範,才能構建高性能、穩定、用戶友好的分頁功能。建議團隊封裝通用 PageHelper 工具類或引入 PageHelper (MyBatis 分頁插件) / Spring Data Pageable,將分頁邏輯從業務代碼中解耦,實現「一行代碼分頁」。