不知道大家做項目做到最後有什麼感覺沒有,其實大家做來做去就是做一個清單加上分頁和多條件的查詢(http://xdwangiflytek.iteye.com/blog/1358261),隻是不同的項目業務流程不一樣而已,是以今天我想說說這裡的分頁。
1、 大家需要了解的是為什麼我們需要分頁?
因為當資料量太大時,會影響查詢和傳輸的性能,并且我們從使用者角度來考慮的話,如果讓使用者一次性看到成千上萬條記錄那使用者也會瘋掉的。
2、 對我們來說有哪些可實作的分頁技術?
a、 存儲過程分頁,即在資料庫中建立一個存儲過程,傳入SQL和頁碼擷取目前頁的記錄,這個需要大家對存儲過程有比較好的認識(我這塊不行),當然這個從性能上來說是最好的,但是不能跨資料庫平台。
b、 使用資料庫專有SQL語句進行分頁(Oracle的rownum、MSSQL的top、MySql的limit等),性能也很好,但是還是不能跨資料庫(其實真實項目中沒那麼多項目要求都跨資料庫)。
c、 JDBC分頁,通過Statement的statement.setMaxRow(endIndex)和resultSet.absoulte(beginIndex)取得目前頁範圍内的記錄。此種方式的性能依賴于廠商對JDBC規範的實作。這種方式的通用性是最好的,性能也不錯,完全與資料庫平台無關了。
d、 根據資料庫類型自動生成資料庫專有特性的sql語句,其實說白了也就是Hibernate的實作方式,這個自己也寫過一個,其實就是根據資料庫類型生成不同的資料庫SQL專有語句而已。
下面我們需要寫一個分頁工具類,在寫之前我們需要弄明白分頁的原理。為了能夠取得指定頁碼所對應的記錄,我們是不是需要兩個關鍵的參數:總記錄數和每頁的記錄數;
每頁的記錄數我們可以設定一個預設值,10、15、20、25都無所謂,根據實際需求。
總記錄數就沒辦法了,需要額外從資料庫中利用Count函數取了,通過這兩個參數我們是不是可以計算出總頁數。同時我們也可以判斷使用者傳過來的頁碼是否有效(小于第一頁OR超出最後一頁),然後我們再根據頁碼和每頁記錄數是不是就可以計算出目前頁的的開始條數和終止條數了。
下面我們就需要來看看分頁的工具類了
package com.iflytek.page;
/**
* 分頁工具類
*
* @author xudongwang 2012-1-19
*
* Email:[email protected]
*/
public class Page {
/**
* 總記錄數
*/
private int totalRow;
/**
* 每頁記錄數
*/
private int pageSize = 10;
/**
* 目前頁碼
*/
private int currentCount;
/**
* 總頁數
*/
private int total;
/**
* 起始記錄下标
*/
private int beginIndex;
/**
* 截止記錄下标
*/
private int endIndex;
/**
* 構造方法,使用總記錄數,目前頁碼
*
* @param totalRow
* 總記錄數
* @param currentCount
* 目前頁面,從1開始
*/
public Page(int totalRow, int currentCount) {
this.totalRow = totalRow;
this.currentCount = currentCount;
calculate();
}
/**
* 構造方法 ,利用總記錄數,目前頁面
*
* @param totalRow
* 總記錄數
* @param currentCount
* 目前頁面
* @param pageSize
* 預設10條
*/
public Page(int totalRow, int currentCount, int pageSize) {
this.totalRow = totalRow;
this.currentCount = currentCount;
this.pageSize = pageSize;
calculate();
}
private void calculate() {
total = totalRow / pageSize + ((totalRow % pageSize) > 0 ? 1 : 0);
if (currentCount > total) {
currentCount = total;
} else if (currentCount < 1) {
currentCount = 1;
}
beginIndex = (currentCount - 1) * pageSize;
endIndex = beginIndex + pageSize;
if (endIndex > totalRow) {
endIndex = totalRow;
}
}
public int getTotalRow() {
return totalRow;
}
public int getPageSize() {
return pageSize;
}
public int getCurrentCount() {
return currentCount;
}
public int getTotal() {
return total;
}
public int getBeginIndex() {
return beginIndex;
}
public int getEndIndex() {
return endIndex;
}
}
繼續
在背景擷取前台傳進來的頁碼 //從頁面取得頁碼
int currentPage = 1;
try {
currentPage = Integer.parseInt(request.getParameter("currentPage"));
} catch (Exception ex) {}
//取得總記錄數,建立Page對象
int totalRow = studentDao.getAllStudents();//通過select count 取得總記錄數
Page page = new Page(totalRow, currentPage);
studentDao.list(page);
資料通路層,StduentDao.java
public List<Stduent> getStudentsByPage(Page page) {
List<Stduent> students = new ArrayList<Stduent>();
try {
String sql = "SELECT id,name,email FROM tbl_stduent";
Connection conn = null;
try {
conn = DbUtil.getConnection();
Statement statement = conn.createStatement();
statement.setMaxRows(page.getEndIndex());//關鍵代碼,設定最大記錄數為目前頁記錄的截止下标
ResultSet resultSet = statement.executeQuery(sql);
if (page.getBeginIndex() > 0) {
resultSet.absolute(page.getBeginIndex());//關鍵代碼,直接移動遊标為目前頁起始記錄處
}
while (resultSet.next()) {
Stduent student = new Student();
……
students.add(student);
}
resultSet.close();
statement.close();
} finally {
if (conn != null) {
conn.close();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return students;
}
其實仔細想想JDBC分頁的性能與頁碼有關即與statement.setMaxRows有效,越往後翻,性能越差,因為越往後一次性查詢的記錄數就多,但是我們從使用者的角度來看不會有使用者會牛逼的一頁一頁翻到第n頁去,一般都是根據條件來縮小查詢範圍。是以折中的辦法就是将記錄數設大一點,另外就是限制使用者翻頁的範圍,其實這些性能的前提都是在資料量非常大的情況下而言的,一般資料量少的話,基本上都可以忽略不計的。