一、 分頁查詢簡介
首先,在很多地方都可以用到分頁查詢,軟件開發最基本的增、刪、改、查裏面的“查”,查詢所有用戶需要分頁,查詢相關業務信息需要分頁。
分頁查詢分爲下面兩種方式:
1.1、 邏輯分頁
在sql查詢時,先從數據庫檢索出所有數據的結果集,然後在程序內,通過邏輯語句獲得分頁需要的的數據。
例如: 檢索11-20條userList.subList(10,20);
1.2、 物理分頁
在sql查詢時,從數據庫只檢索分頁需要的數據。通常不同的數據庫有着不同的物理分頁語句,mysql物理分頁,採用limit關鍵字。
例如:檢索11-20條 select *from user limit 10,10 ;(limit m,n 是mysql的語法)
下面介紹的分頁查詢就是以mysql爲例進行說明的。
1.3、 sql語句的分頁解析
select * from tablelimit m,n; 其中m是指記錄開始的index(從0開始表示第一條記錄),n是指從第m+1條開始,取n條。
好了,這是mysql數據庫中sql語句的分頁原理。下面看具體實施。
二、 分頁查詢方案
2.1、 不用分頁獲取集合信息
在這裏我用的是最基礎的servlet來實現控制層與視圖層進行交互,在各大框架中原理也是類似,更簡單。
我們現在要做一個客戶管理系統,前端頁面需要返回客戶基本信息。比如:
假設我們現在不用分頁的話,在後臺控制器裏面我們需要返回一個List<Cust>:
List<Cust> list =custService.findAll ();
request.setAttribute("list", list);
request.getRequestDispatcher("/list.jsp").forward(request,response);
我們只需要給頁面返回一個Cust實體類的集合list,然後在頁面進行遍歷就可以拿到所有的客戶信息。這樣的話只有一個Cust實體類已經足夠,因爲我們要的僅僅只是Cust的一個集合。
然後業務層,DAO層你只需稍微處理一下就可以了,比如DAO層的SQL應該只需這樣寫就可以了:"select * fromcustomer ";
2.1、 分頁獲取集合信息
好了,因爲客戶信息太多了(幾百條、幾萬條甚至更多),我們又不想用邏輯分頁,我們應該做一下幾個步驟。
2.2.1、 針對控制層:
l 第一步,你要從前端獲取當前需要顯示的頁(第幾頁)和每頁的記錄數。
l 第二步,調用業務層(service)中分頁查詢的方法。
l 第三步,存入request帶到前端頁面(jsp)。
l 代碼如下所示:
//1、獲取當前要顯示的頁和每頁記錄數
int thisPage = Integer.parseInt(request.getParameter("thisPage"));
int pageSize = Integer.parseInt(request.getParameter("pageSize"));
//2、調用service中分頁查詢客戶的方法
List<Cust> list = custService.findCustPage(thisPage, pageSize);
####首先,說明一點,這裏只返回一個Cust的List是不夠的,先放在這裏,等下再修改這裏的代碼,這樣更易於理解###
//3、存入request帶到我們的jsp頁面中
request.setAttribute("list", list);
request.getRequestDispatcher("/pageList.jsp").forward(request, response);
*思考問題*
接下來,我們思考一個問題,我們首頁需要顯示數據的詳細信息之外(也就是一個實體類的集合需要展示在頁面上的信息),還需要顯示什麼信息呢?我們是不是還需要顯示下面這個:
共XXX條記錄共XX頁首頁上一頁 1 2 3 4 5 下一頁尾頁
那我們後端控制層只返回一個Cust的List夠不夠?顯然是不夠的,這樣我們在service層分頁查詢客戶信息方法處理的時候就需要注意了:
我們是不是需要bean類來封裝一些信息,以便於放入request域在前端顯示,比如:bean裏賣年封裝一個List<Cust>、當前頁碼thisPage、每頁大小pageSize、總記錄數allRow等等。那麼這個bean如下所示:
2.2.2、 針對工具bean
因爲現在只處理一個實體類Cust,這個bean沒有用泛型寫成通用的bean,應該是要寫成通用的pageBean的,不過在這裏爲了方便講清楚分頁的原理暫時不涉及泛型:
import java.util.List;
public class CustPage {
/**
* 當前頁碼
*/
private int thisPage;
/**
* 每頁大小
*/
private int pageSize;
/**
* 總記錄數
*/
private long allRow;
/**
* 總頁
*/
private int totalPage;
/**
* 首頁
*/
private int firstPage;
/**
* 尾頁
*/
private int lastPage;
/**
* 上一頁
*/
private int previousPage;
/**
* 下一頁
*/
private int nextPage;
/**
* 返回的Custs列表
*/
private List<Cust> list;
public CustPage() {
}
public CustPage(int thisPage, int pageSize, long allRow, int totalPage, int firstPage, int lastPage,
int previousPage, int nextPage, List<Cust> list) {
super();
this.thisPage = thisPage;
this.pageSize = pageSize;
this.allRow = allRow;
this.totalPage = totalPage;
this.firstPage = firstPage;
this.lastPage = lastPage;
this.previousPage = previousPage;
this.nextPage = nextPage;
this.list = list;
}
public int getThisPage() {
return thisPage;
}
public void setThisPage(int thisPage) {
this.thisPage = thisPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public long getAllRow() {
return allRow;
}
public void setAllRow(long allRow) {
this.allRow = allRow;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getFirstPage() {
return firstPage;
}
public void setFirstPage(int firstPage) {
this.firstPage = firstPage;
}
public int getLastPage() {
return lastPage;
}
public void setLastPage(int lastPage) {
this.lastPage = lastPage;
}
public int getPreviousPage() {
return previousPage;
}
public void setPreviousPage(int previousPage) {
this.previousPage = previousPage;
}
public int getNextPage() {
return nextPage;
}
public void setNextPage(int nextPage) {
this.nextPage = nextPage;
}
public List<Cust> getList() {
return list;
}
public void setList(List<Cust> list) {
this.list = list;
}
}
2.2.3、 針對業務層
我們需要在業務層處理所有的業務,將上面CustPage這個bean裏面我們需要在前端顯示的信息裝進去。代碼如下:
@Override
public CustPage findCustPage(int thisPage, int pageSize) {
CustPage custPage = new CustPage();
//設置當前頁
custPage.setThisPage(thisPage);
//設置每頁大小
custPage.setPageSize(pageSize);
//查找總記錄數,設置總記錄數
long allRow = custDao.getAllRow();
custPage.setAllRow(allRow);
//計算總頁數並設置
int totalPage = (int) (allRow/pageSize+(allRow%pageSize==0?0:1));
custPage.setTotalPage(totalPage);
//設置首頁
custPage.setFirstPage(1);
//設置尾頁
custPage.setLastPage(totalPage);
//設置上一個
custPage.setPreviousPage(thisPage==1?1:thisPage-1);
//設置下一頁
custPage.setNextPage(thisPage==totalPage?totalPage:thisPage+1);
//獲得需要分頁的實體類數據
List<Cust> list = custDao.getCustByPage((thisPage-1)*pageSize,pageSize);
custPage.setList(list);
return custPage;
}
2.2.4、 針對DAO層
我在這裏使用了阿帕奇的一個jdbc工具,DBUtils。是一個非常輕量,非常好用的DAO層JDBC工具,現在的Hibernate太龐大了。有興趣的朋友可以查看我其他博文進行了解。
@Override
public long getAllRow() {
String sql = "select count(*) from customer";
try{
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
return (Long)runner.query(sql, new ScalarHandler());
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
@Override
public List<Cust> getCustByPage(int thisPage, int pageSize) {
String sql = "select * from customer limit ?,?";
try{
QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
return runner.query(sql, new BeanListHandler<Cust>(Cust.class), thisPage,pageSize);
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
好了,到這裏,大概已經清楚了分頁的原理,具體運用到項目中,還是需要自己實踐。