在傳統 JSP 專案中實現美觀、功能完整的分頁,bootstrap-paginator 是一款輕量級的最佳選擇。本文提供基於 Bootstrap 3 的完整分頁實現,包含後端分頁邏輯、前端渲染、JS 初始化配置,以及 SQL Server/MySQL 分頁寫法差異說明。
支援首頁/末頁、上一頁/下一頁、頁碼跳轉、頁面大小切換、當前頁高亮、Tooltip 提示,完美適配 Bootstrap 3 風格。
<%@ page import="java.util.*, java.sql.*" %>
<%@ page import="com.example.model.Record, com.example.util.DBUtil" %>
<%
final int pageSize = 20;
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 1. 獲取並校驗頁碼
String pageNoStr = request.getParameter("pageNoStr");
int pageNo = 1;
try {
if (pageNoStr != null && !pageNoStr.isEmpty()) {
pageNo = Integer.parseInt(pageNoStr);
if (pageNo < 1) pageNo = 1;
}
} catch (NumberFormatException e) {
pageNo = 1;
}
// 2. 查詢總記錄數 → 計算總頁數
int totalPages = 1;
try (Connection conn = DBUtil.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM alldata")) {
if (rs.next()) {
int totalRecords = rs.getInt(1);
totalPages = (totalRecords + pageSize - 1) / pageSize; // 向上取整
}
} catch (SQLException e) {
e.printStackTrace();
}
// 3. 邊界修正
if (pageNo > totalPages) pageNo = totalPages;
// 4. 查詢當前頁數據
List<Record> records = new ArrayList<>();
int startIndex = (pageNo - 1) * pageSize + 1;
int endIndex = pageNo * pageSize;
// SQL Server 分頁寫法(ROW_NUMBER)
String sql = "SELECT * FROM (" +
"SELECT ROW_NUMBER() OVER(ORDER BY data_taizhan_num, data_date ASC) AS rn, * FROM alldata" +
") AS temp WHERE rn BETWEEN ? AND ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, startIndex);
pstmt.setInt(2, endIndex);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
Record r = new Record();
r.setTaizhan_num(rs.getString("data_taizhan_num"));
r.setDate(rs.getTimestamp("data_date"));
r.setTem(rs.getString("data_tem"));
r.setHum(rs.getString("data_hum"));
r.setPa(rs.getString("data_pa"));
r.setRain(rs.getString("data_rain"));
r.setWin_dir(rs.getString("data_win_dir"));
r.setWin_sp(rs.getString("data_win_sp"));
records.add(r);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>搜索記錄 - 分頁演示</title>
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/recordSearchResult.css">
</head>
<body>
<div class="container">
<form class="form-area">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>觀測台站</th>
<th>溫度(℃)</th>
<th>濕度(%)</th>
<th>壓強(hPa)</th>
<th>雨量(mm)</th>
<th>風向(°)</th>
<th>風速(m/s)</th>
<th>觀測時間</th>
</tr>
</thead>
<tbody>
<%
if (records == null || records.isEmpty()) {
out.println("<tr><td colspan='8' class='text-center'><h4>沒有符合要求的記錄</h4></td></tr>");
} else {
for (Record r : records) {
%>
<tr>
<td><%= r.getTaizhan_num() %></td>
<td><%= r.getTem() %></td>
<td><%= r.getHum() %></td>
<td><%= r.getPa() %></td>
<td><%= r.getRain() %></td>
<td><%= r.getWin_dir() %></td>
<td><%= r.getWin_sp() %></td>
<td><%= r.getDate() %></td>
</tr>
<% } } %>
</tbody>
</table>
<!-- 分頁容器 -->
<div class="text-center">
<ul class="pagination" id="paginator"></ul>
</div>
</form>
</div>
<script src="js/jquery-1.12.3.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/bootstrap-paginator.min.js"></script>
<script>
var options = {
bootstrapMajorVersion: 3, // 關鍵:指定 Bootstrap 3
size: 'normal',
alignment: 'center',
itemTexts: function(type, page, current) {
switch (type) {
case "first": return "首頁";
case "prev": return "<i class='fa fa-caret-left'></i> 上一頁";
case "next": return "下一頁 <i class='fa fa-caret-right'></i>";
case "last": return "末頁";
case "page": return page;
}
},
tooltipTitles: function(type, page, current) {
switch (type) {
case "first": return "首頁";
case "prev": return "上一頁";
case "next": return "下一頁";
case "last": return "末頁";
case "page": return "第 " + page + " 頁";
}
},
pageUrl: function(type, page, current) {
return "showInfoSearchResult.jsp?pageNoStr=" + page;
},
numberOfPages: 6,
currentPage: <%= pageNo %>,
totalPages: <%= totalPages %>
};
$('#paginator').bootstrapPaginator(options);
</script>
</body>
</html>
public class Record {
private String taizhan_num, tem, hum, pa, rain, win_dir, win_sp;
private Timestamp date;
// 標準 getter/setter...
}
public class DBUtil {
private static DataSource ds;
static {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:sqlserver://localhost:1433;DatabaseName=weather");
p.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
p.setUsername("sa");
p.setPassword("your_password");
p.setMaxActive(100);
p.setInitialSize(10);
p.setMinIdle(10);
p.setMaxWait(10000);
p.setValidationQuery("SELECT 1");
p.setTestOnBorrow(true);
p.setJdbcInterceptors("ConnectionState;StatementFinalizer");
ds = new DataSource();
ds.setPoolProperties(p);
}
public static Connection getConnection() throws SQLException { return ds.getConnection(); }
public static void close(AutoCloseable c) { if (c != null) try { c.close(); } catch (Exception ignored) {} }
}
| 資料庫 | 分頁語法 | 範例 |
|---|---|---|
| SQL Server | ROW_NUMBER() 窗口函數 | SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) rn, * FROM t) tmp WHERE rn BETWEEN 21 AND 40 |
| MySQL | LIMIT offset, count | SELECT * FROM t ORDER BY id LIMIT 20, 20 (跳過 20 條,取 20 條) |
| Oracle 12c+ | OFFSET ... FETCH | SELECT * FROM t ORDER BY id OFFSET 20 ROWS FETCH NEXT 20 ROWS ONLY |
| PostgreSQL | LIMIT / OFFSET | SELECT * FROM t ORDER BY id LIMIT 20 OFFSET 20 |
pageSizepageNo (從 1 開始)startIndex = (pageNo - 1) * pageSize + 1endIndex = pageNo * pageSizetotalPages = (totalRecords + pageSize - 1) / pageSize<ul> 容器,bootstrapMajorVersion: 3 配置不可省略<div> 容器,語法不同document/ 目錄,範例豐富易懂此方案適用於傳統 JSP/Servlet 專案,若為前後端分離架構,建議改為 REST API 返回 JSON,前端用 Vue/React + 分頁組件渲染。