在企業級應用開發中,數據分頁是提升系統性能與用戶體驗的關鍵技術。Hibernate 作為主流 ORM 框架,提供了多種分頁實現方式,本文將通過實例深入剖析其核心原理與最佳實踐。
無論採用何種技術棧,數據分頁的基本思路保持一致:
COUNT(*) 查詢獲得數據總量,作為計算總頁數的依據總頁數 = (總記錄數 - 1) / 每頁記錄數 + 1-- 獲取前 N 筆記錄
SELECT TOP n * FROM table WHERE ... ORDER BY ...
-- 分頁查詢範例(SQL Server)
SELECT TOP pageSize * FROM car
WHERE id NOT IN (
SELECT TOP (page-1)*pageSize id FROM car ORDER BY id ASC
) ORDER BY id ASC
MySQL 提供原生 LIMIT 子句,語法簡潔高效:
SELECT ... FROM table WHERE ... ORDER BY ... LIMIT offset, rows
offset:起始行偏移量(從 0 開始)rows:返回行數以下展示一個基於 MySQL 的完整分頁工具類實現:
package com.cdd.util;
public class Car {
private String id;
private String name;
private String brand;
private String engineNum;
private String state;
private String remarks;
// 構造器、Getter/Setter 省略...
package com.cdd.util;
import java.sql.*;
public class GetConn {
static {
try { Class.forName("com.mysql.jdbc.Driver"); }
catch (ClassNotFoundException e) { e.printStackTrace(); }
}
public Connection getConn() {
Connection conn = null;
try {
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/oa", "root", "1120");
} catch (SQLException e) { e.printStackTrace(); }
return conn;
}
}
package com.cdd.util;
import java.sql.*;
import java.util.*;
public class PaginationUtil {
private Connection conn = new GetConn().getConn();
// 分頁查詢返回 List<Car>
public List<Car> findGrade(int page, int pageSize) {
String sql = "SELECT * FROM car ORDER BY id LIMIT "
+ (page-1)*pageSize + "," + pageSize;
List<Car> list = new ArrayList<>();
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()) {
Car car = new Car();
car.setId(rs.getString("id"));
car.setName(rs.getString("name"));
car.setBrand(rs.getString("brand"));
car.setEngineNum(rs.getString("engineNum"));
car.setState(rs.getString("state"));
car.setRemarks(rs.getString("remarks"));
list.add(car);
}
} catch (Exception e) { e.printStackTrace(); }
finally { closeResources(rs, stmt); }
return list;
}
// 計算總頁數
public int allPage(int pageSize) {
int total = 0;
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM car");
if (rs.next()) total = rs.getInt(1);
closeResources(rs, stmt);
} catch (SQLException e) { e.printStackTrace(); }
return (total - 1) / pageSize + 1;
}
private void closeResources(ResultSet rs, Statement stmt) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
} catch (SQLException e) { e.printStackTrace(); }
}
}
<%@ page import="java.util.*,com.cdd.util.*" %>
<%
PaginationUtil util = new PaginationUtil();
int pageNo = request.getParameter("No") == null ? 1
: Integer.parseInt(request.getParameter("No"));
List<Car> cars = util.findGrade(pageNo, 3);
int totalPages = util.allPage(3);
%>
<table border="1" align="center">
<tr><th>ID</th><th>名稱</th><th>品牌</th><th>引擎號</th></tr>
<% for (Car car : cars) { %>
<tr>
<td><%=car.getId()%></td>
<td><%=car.getName()%></td>
<td><%=car.getBrand()%></td>
<td><%=car.getEngineNum()%></td>
</tr>
<% } %>
</table>
<center>
共 <%=totalPages%> 頁,當前第 <%=pageNo%> 頁
<% if (pageNo > 1) { %>
<a href="index.jsp?No=<%=pageNo-1%>">上一頁</a>
<% } %>
<% if (pageNo < totalPages) { %>
<a href="index.jsp?No=<%=pageNo+1%>">下一頁</a>
<% } %>
</center>
Hibernate 在 HQL 和 QBC (Criteria) 中均提供了標準分頁 API,無需編寫特定 SQL:
Query query = session.createQuery("FROM Car");
query.setFirstResult(2); // 從索引 2 開始(第 3 條)
query.setMaxResults(5); // 最多返回 5 條
List<Car> list = query.list();
Criteria criteria = session.createCriteria(Car.class);
criteria.setFirstResult(2);
criteria.setMaxResults(5);
List<Car> list = criteria.list();
public List<T> findByPage(final String hql, final int offset, final int pageSize) {
return getHibernateTemplate().executeFind(session -> {
Query query = session.createQuery(hql);
query.setFirstResult(offset);
query.setMaxResults(pageSize);
return query.list();
});
}
// 支持參數綁定的重載版本
public List<T> findByPage(final String hql, final Object[] params,
final int offset, final int pageSize) {
return getHibernateTemplate().executeFind(session -> {
Query query = session.createQuery(hql);
for (int i = 0; i < params.length; i++) {
query.setParameter(i, params[i]);
}
query.setFirstResult(offset);
query.setMaxResults(pageSize);
return query.list();
});
}
OFFSET 深分頁,改用「游標分頁」或「延遲關聯」技術COUNT(*) 開銷掌握 Hibernate 分頁技巧,能顯著提升數據密集型應用的響應速度與擴展性。實際項目中建議結合具體業務場景,選擇最適合的分頁策略。