數據庫層分頁是性能最優方案。Oracle 無 LIMIT,採用三層嵌套 ROWNUM 實現:
SELECT * FROM (
SELECT ROWNUM rn, t.* FROM (
SELECT * FROM user_info WHERE ... ORDER BY id
) t WHERE ROWNUM <= :endRow
) WHERE rn >= :startRow
ROWNUM 並截取前 endRow 條rn >= startRow 獲得當前頁數據public class PageModel<E> {
private List<E> list; // 當前頁數據
private int pageNo; // 當前頁碼
private int pageSize; // 每頁大小
private int totalNum; // 總記錄數
private int totalPage; // 總頁數
// getter/setter 省略
// 輔助方法:首頁、末頁、上一頁、下一頁、是否可用
public void setTotalNum(int totalNum) {
this.totalNum = totalNum;
this.totalPage = (totalNum % pageSize == 0) ?
totalNum / pageSize : totalNum / pageSize + 1;
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
int pageNo = Integer.parseInt(req.getParameter("pageNo"));
UserInfo query = new UserInfo();
query.setSex(req.getParameter("gender"));
query.setHome(req.getParameter("newlocation"));
// ... 其他條件
UserInfoManage service = new UserInfoManage();
PageModel<UserInfo> page = service.userBasicSearch(query, pageNo, pageSize);
// 構建 XML 響應(或用 Jackson 序列化 JSON)
StringBuilder xml = new StringBuilder("<?xml ...?><root>");
for (UserInfo u : page.getList()) {
xml.append("<data><id>").append(u.getId()).append("</id>...")
.append("<truename>").append(u.getTruename()).append("</truename>...")
.append("</data>");
}
xml.append("<pagination><total>").append(page.getTotalPage())
.append("</total><pageno>").append(page.getPageNo()).append("</pageno></pagination></root>");
writeResponse(resp, xml.toString());
}
public PageModel<UserInfo> userBasicSearch(UserInfo u, int pageNo, int pageSize) {
Connection conn = null;
PageModel<UserInfo> page = new PageModel<>();
try {
conn = DBUtil.getConnection();
DBUtil.setAutoCommit(conn, false);
page.setList(dao.getUserList(u, pageNo, pageSize));
page.setPageNo(pageNo);
page.setPageSize(pageSize);
page.setTotalNum(dao.getTotalNum(u));
DBUtil.commit(conn);
} catch (Exception e) {
DBUtil.rollBack(conn);
throw new RuntimeException(e);
} finally {
DBUtil.closeConnection();
}
return page;
}
public List<UserInfo> getUserList(UserInfo u, int pageNo, int pageSize) {
String sql = "SELECT * FROM (" +
" SELECT ROWNUM rn, t.* FROM (" +
" SELECT * FROM user_info WHERE sex = ? AND home LIKE ? AND colleage LIKE ? AND comingyear LIKE ? ORDER BY id" +
" ) t WHERE ROWNUM <= ?" +
") WHERE rn >= ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, u.getSex());
ps.setString(2, u.getHome() + "%");
ps.setString(3, u.getColleage() + "%");
ps.setString(4, u.getCy() + "%");
ps.setInt(5, pageNo * pageSize); // endRow
ps.setInt(6, (pageNo - 1) * pageSize + 1); // startRow
try (ResultSet rs = ps.executeQuery()) {
List<UserInfo> list = new ArrayList<>();
while (rs.next()) {
UserInfo user = new UserInfo();
user.setId(rs.getInt("id"));
user.setTruename(rs.getString("truename"));
user.setSex(rs.getString("sex"));
user.setHome(rs.getString("home"));
list.add(user);
}
return list;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public int getTotalNum(UserInfo u) {
String sql = "SELECT COUNT(*) FROM user_info WHERE sex = ? AND home LIKE ? AND colleage LIKE ? AND comingyear LIKE ?";
// ... 類似執行,返回 count
}
public class DBUtil {
private static ThreadLocal<Connection> tl = new ThreadLocal<>();
public static Connection getConnection() {
Connection conn = tl.get();
if (conn == null) {
conn = DriverManager.getConnection(url, user, pwd);
tl.set(conn);
}
return conn;
}
public static void closeConnection() {
Connection conn = tl.get();
if (conn != null) {
try { conn.close(); } catch (SQLException ignored) {}
tl.remove();
}
}
// setAutoCommit, commit, rollBack, closePreparedStatement, closeResultSet ...
}
ThreadLocal 作用:同一線程內多次 getConnection() 返回同一連接,保證事務一致性。
DriverManager 為 HikariCP/Druid。ps.setString(2, u.getHome() + "%") 防注入。COUNT(1)、索引覆蓋、或緩存總數。WHERE id > lastId ORDER BY id LIMIT pageSize。Oracle 分頁雖繁瑣,但掌握「三層 ROWNUM」模式並封裝通用 PageModel、DBUtil,即可構建可復用的分頁組件。現代項目建議引入成熟分頁插件,專注業務邏輯。