分頁(yè)是一種常用的頁(yè)面數(shù)據(jù)顯示技術(shù),分頁(yè)能夠通過(guò)減少頁(yè)面數(shù)據(jù)處理量從而提高了系統(tǒng)的性能。分頁(yè)應(yīng)該是做WEB開(kāi)發(fā)必須掌握的一個(gè)小技術(shù)。而分頁(yè)卻是復(fù)雜的,倒不是它的技術(shù)有多復(fù)雜;而是有太多的重復(fù)代碼,這些代碼都難以重用。能不能實(shí)現(xiàn)一個(gè)通用的分頁(yè)框架?每次只需要去覆寫(xiě)一兩個(gè)方法,通過(guò)少量的代碼就能實(shí)現(xiàn)分頁(yè)的功能?
一般分頁(yè)應(yīng)該要具有的功能有
1. 靈活的設(shè)置分頁(yè)大小。可以動(dòng)態(tài)的設(shè)置分頁(yè)大小,而不是寫(xiě)死到代碼中。
2. 自動(dòng)計(jì)算總頁(yè)數(shù)。根據(jù)分頁(yè)大小和總記錄數(shù)自動(dòng)計(jì)算總頁(yè)數(shù)。
3. 獲得當(dāng)前頁(yè)的頁(yè)號(hào)。
4. 獲得當(dāng)前頁(yè)的總記錄數(shù)。一般是最后一頁(yè)的時(shí)候可能會(huì)小于分頁(yè)大小。
5. 判斷當(dāng)前頁(yè)是否為第一頁(yè)。
6. 判斷當(dāng)前頁(yè)是否為最后一頁(yè)。
7. 判斷當(dāng)前頁(yè)是否有上一頁(yè)。
8. 判斷當(dāng)前頁(yè)是否有下一頁(yè)。
9. 獲得當(dāng)前頁(yè)的數(shù)據(jù)列表。
10. 獲得當(dāng)前頁(yè)的第一條記錄的索引號(hào)
11. 獲得當(dāng)前頁(yè)的最后一條記錄的索引號(hào)。
二、常用的分頁(yè)技術(shù)
目前常用的分頁(yè)技術(shù)有兩種:
1. 第一次訪問(wèn)是讀取所有記錄,放入session中,然后每次從session對(duì)象中讀取當(dāng)前頁(yè)的數(shù)據(jù)
2. 每次都訪問(wèn)數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)中讀取當(dāng)前頁(yè)的記錄。
這兩種方法都各有優(yōu)缺點(diǎn),當(dāng)數(shù)據(jù)量比較少時(shí),第一種方法無(wú)疑是要快一些,因?yàn)闇p少與數(shù)據(jù)庫(kù)的連接訪問(wèn)。而當(dāng)數(shù)據(jù)量比較大時(shí),比如查詢結(jié)果可能會(huì)是上萬(wàn)條,那么內(nèi)存的開(kāi)銷(xiāo)是十分大的,放到session中還有一個(gè)問(wèn)題是能不能及時(shí)的清除無(wú)用的對(duì)象。而且這么大數(shù)據(jù)量在網(wǎng)絡(luò)中傳輸也會(huì)使系統(tǒng)變得很慢。
第二種方法就是專(zhuān)門(mén)解決這個(gè)問(wèn)題的,它每次訪問(wèn)數(shù)據(jù)庫(kù),只讀取當(dāng)前頁(yè)所需的記錄,大大的減少網(wǎng)絡(luò)傳輸量;它不會(huì)把頁(yè)數(shù)據(jù)放到session中,大大提高服務(wù)器的性能。
所以第二種方式要優(yōu)于第一種方法。Session不要亂用,要用也僅僅是存放一些公共變量,相對(duì)于占用空間比較少的對(duì)象。不適合存放大量的數(shù)據(jù),否則在很多個(gè)用戶同時(shí)訪問(wèn)時(shí)那么系統(tǒng)會(huì)很慢,因?yàn)榉?wù)器內(nèi)存被銷(xiāo)耗的很厲害
三、通用分頁(yè)框架需要解決的問(wèn)題
作為一個(gè)通用分頁(yè)框架,
1. 應(yīng)該不依賴于任何其它框架
2. 應(yīng)該支持多種數(shù)據(jù)庫(kù)
3. 應(yīng)該可以應(yīng)用于任何web框架中,如:struts,spring等。
4. 應(yīng)該把數(shù)據(jù)訪問(wèn)的具體實(shí)現(xiàn)留給用戶去實(shí)現(xiàn)。
5. 應(yīng)該實(shí)現(xiàn)關(guān)鍵的算法和過(guò)程,如:計(jì)算總頁(yè)數(shù),所需的實(shí)始化動(dòng)作。
6. 應(yīng)該減化Contrller控制器的代碼,以往的分頁(yè)技術(shù)在Contrller中存在太多的if…else代碼。十分難懂,應(yīng)該由一個(gè)輔助類(lèi)來(lái)實(shí)現(xiàn)。
7. 應(yīng)該減化jsp頁(yè)面的代碼,頁(yè)面應(yīng)該沒(méi)有任何與分頁(yè)相關(guān)的計(jì)算。應(yīng)該由分頁(yè)對(duì)象來(lái)實(shí)現(xiàn)。
8. 應(yīng)該支持兩種分頁(yè)方式,采用session或不采用session由用戶控制。
四、具體實(shí)現(xiàn)
1.通用分頁(yè)接口。定義接口可以有更多不同的實(shí)現(xiàn),接口只聲明了分頁(yè)應(yīng)該具有的公共行為。
ViewPage.java
/**
* 分頁(yè)接口
* @author ex_yuanguangdong
*
*/
public interface ViewPage {
/**
* 獲取總頁(yè)數(shù)
* @return 總頁(yè)數(shù)
*/
public int getPageCount();
/**
* 獲得頁(yè)面大小
* @return 頁(yè)面大小
*/
public int getPageSize();
/**
* 設(shè)置頁(yè)面大小
* @param size
*/
public void setPageSize(int size);
/**
* 獲得當(dāng)前頁(yè)數(shù)據(jù)
* @return 數(shù)據(jù)列表
*/
public List getPageData();
/**
* 獲得當(dāng)前頁(yè)索引號(hào)
* @return 當(dāng)前頁(yè)索引號(hào)
*/
public int getPageIndex();
/**
* 獲得當(dāng)前頁(yè)記錄總數(shù)
* @return 當(dāng)前頁(yè)記錄總數(shù)
*/
public int getPageRows();
/**
* 是否有下一頁(yè)
* @return
*/
public boolean getHashNextPage();
/**
* 是否有上一頁(yè)
* @return
*/
public boolean getHashPreviousPage();
/**
* 轉(zhuǎn)到尾頁(yè)
*
*/
public void gotoLastPage();
/**
* 轉(zhuǎn)到首頁(yè)
*
*/
public void gotoFirstPage();
/**
* 是否首頁(yè)
* @return
*/
public boolean isFirstPage();
/**
* 是否尾頁(yè)
* @return
*/
public boolean isLastPage();
/**
* 轉(zhuǎn)到上一頁(yè)
*
*/
public void gotoPreviousPage();
/**
* 轉(zhuǎn)到下一頁(yè)
*
*/
public void gotoNextPage();
/**
* 轉(zhuǎn)到指定頁(yè)面,pageIndex小于1時(shí),轉(zhuǎn)到第一頁(yè);pageIndex大于總頁(yè)數(shù)時(shí),轉(zhuǎn)到最尾頁(yè)
* @param pageIndex 指定的頁(yè)號(hào)
*/
public void gotoPage(int pageIndex);
/**
* 獲取當(dāng)前頁(yè)第一條記錄的記錄號(hào)
* @return int 當(dāng)前頁(yè)第一條記錄的記錄號(hào)
*/
public int getPageFirstRecord();
/**
* 獲取當(dāng)前頁(yè)最后一條記錄的記錄號(hào)
* @return int 當(dāng)前頁(yè)最后一條記錄的記錄號(hào)
*/
public int getPageLastRecord();
}
2. 分頁(yè)抽像實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)關(guān)鍵的算法
AbstractViewPage.java
/**
* 分頁(yè)默認(rèn)抽象實(shí)現(xiàn)
* 初始時(shí),分頁(yè)類(lèi)有下列默認(rèn)值:
* 分頁(yè)大小為-1,為不分頁(yè);
* 總頁(yè)數(shù)為1頁(yè)
* 當(dāng)前頁(yè)為第一頁(yè)
* 總記錄數(shù)為0條
* 當(dāng)前頁(yè)數(shù)據(jù)列表為沒(méi)有任何記錄的列表
* @author ex_yuanguangdong
*
*/
public abstract class AbstractViewPage implements ViewPage {
//-----------------------------------------
//私有靜態(tài)常量
//-----------------------------------------
private static final int DEFAULT_PAGE_INDEX = 1;
private static final int DEFALT_PAGE_COUNT = 1;
private static final int DEFAULT_PAGE_SIZE = -1;
private static final int DEFAULT_ROWS = 0;
//-----------------------------------------
//私有成員變量
//-----------------------------------------
/**當(dāng)前頁(yè)索引號(hào)**/
private int pageIndex = DEFAULT_PAGE_INDEX;
/**總頁(yè)數(shù)**/
private int pageCount =DEFALT_PAGE_COUNT;
/**分頁(yè)大小**/
private int pageSize = DEFAULT_PAGE_SIZE ;
/**數(shù)據(jù)總記錄數(shù)**/
private int rows = DEFAULT_ROWS;
//------------------------------------------
//本地成員變量getter,setter方法
//------------------------------------------
/**
* 設(shè)置新頁(yè)號(hào),只有大于等于1而且小于等于總頁(yè)數(shù)并且不為當(dāng)前頁(yè)時(shí),才允許設(shè)置
* @param pageIndex The pageIndex to set.
*/
private void setPageIndex(int newPageIndex) {
if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() && newPageIndex != this.pageIndex) {
this.pageIndex = newPageIndex;
}
}
/**
* @return Returns the rows.
*/
private int getRows() {
return rows;
}
/**
* @param rows The rows to set.
*/
private void setRows(int rows) {
this.rows = rows;
}
/**
* @param pageCount The pageCount to set.
*/
private void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
//--------------------------------------
//實(shí)現(xiàn)Page接口方法
//--------------------------------------
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageData()
*/
public List getPageData() {
List pageList =null;
//獲得當(dāng)前頁(yè)數(shù)據(jù)
pageList = this.pageList(this.getPageFirstRecord(), this.getPageRows());
//保證不返回null
if(pageList == null) {
pageList =new ArrayList();
}
return pageList;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageIndex()
*/
public int getPageIndex() {
return this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isFirstPage()
*/
public boolean isFirstPage() {
return this.DEFAULT_PAGE_INDEX ==this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isLastPage()
*/
public boolean isLastPage() {
//當(dāng)前頁(yè)索引為總頁(yè)數(shù)時(shí)為最后一頁(yè)
return this.pageIndex == this.pageCount;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashNextPage()
*/
public boolean getHashNextPage() {
//當(dāng)前頁(yè)索引號(hào)小于總頁(yè)數(shù)
return this.pageIndex < this.pageCount ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashPreviousPage()
*/
public boolean getHashPreviousPage() {
//當(dāng)前頁(yè)索引號(hào)大于默認(rèn)的初始頁(yè)號(hào),這里為1
return this.pageIndex > this.DEFAULT_PAGE_INDEX ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageCount()
*/
public int getPageCount() {
return this.pageCount;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageSize()
*/
public int getPageSize() {
return this.pageSize;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageRows()
*/
public int getPageRows() {
//當(dāng)頁(yè)面大小為-1 時(shí),返回總記錄數(shù)
if(this.DEFAULT_PAGE_SIZE == this.pageSize ) {
return this.rows;
}
//不為最后一頁(yè)時(shí),返回pageSize
if(!this.isLastPage()) {
return this.pageSize;
}
//最后一頁(yè)時(shí)
return this.rows - (this.pageSize * (this.pageCount -1));
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageFirstRecord()
*/
public int getPageFirstRecord() {
//頁(yè)大小為-1 時(shí)
if(this.DEFAULT_PAGE_SIZE== this.pageSize ) {
return 0;
}
return (this.pageIndex -1)* this.pageSize;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageLastRecord()
*/
public int getPageLastRecord() {
//頁(yè)大小為-1時(shí),返回總記錄數(shù)
if(this.DEFAULT_PAGE_SIZE == this.pageSize) {
return this.rows;
}
return this.getPageFirstRecord() + this.getPageRows() ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoFirstPage()
*/
public void gotoFirstPage() {
this.gotoPage(this.DEFAULT_PAGE_INDEX);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoLastPage()
*/
public void gotoLastPage() {
this.gotoPage(this.getPageCount());
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPreviousPage()
*/
public void gotoPreviousPage() {
this.gotoPage(this.getPageIndex() -1);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoNextPage()
*/
public void gotoNextPage() {
this.gotoPage(this.getPageIndex() + 1);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPage(int)
*/
public void gotoPage(int newPageIndex) {
if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() ) {
this.setPageIndex(newPageIndex);
}
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#setPageSize(int)
*/
public void setPageSize(int size) {
if(size < 1) {
size = 1;
}
this.pageSize = size;
//進(jìn)行初始化
this.doInit();
}
//-----------------------------------
//輔助方法
//-----------------------------------
/**
* 分頁(yè)初始化方法,為了保證總是能正確的初始化,所以聲明為final ,為了讓子類(lèi)可以調(diào)用聲明為protected
*
*/
protected final void doInit() {
int rows = 0;
//獲得總記錄數(shù)
rows= totalRows();
//設(shè)置總記錄數(shù)
this.setRows(rows);
//設(shè)置新的總頁(yè)數(shù)
//計(jì)算并設(shè)置總頁(yè)數(shù)
int pages = calculatePageCount();
this.setPageCount(pages);
//轉(zhuǎn)到第一頁(yè)
this.gotoPage(this.DEFAULT_PAGE_INDEX);
onInit();
}
/**
* 記算總頁(yè)數(shù)
* @return 總頁(yè)數(shù)
*/
private int calculatePageCount() {
//總記錄數(shù)為0條,則返回的總頁(yè)數(shù)為1
if(this.getRows() == 0) {
return this.DEFALT_PAGE_COUNT;
}
//如果頁(yè)面大小為-1,則返回的總頁(yè)數(shù)為1
if(this.DEFAULT_PAGE_SIZE == this.getPageSize() ) {
return this.DEFALT_PAGE_COUNT;
}
return this.getRows() / this.getPageSize() + ( this.getRows() % this.getPageSize() ==0 ? 0 :1);
}
/**
* 獲得總記錄數(shù),調(diào)用queryTotalRows(),將異常封裝為non-checked 異常
* @return 總記錄數(shù)
* @throws ApplicationRuntimeException
*/
private int totalRows() throws ApplicationRuntimeException{
try{
return queryTotalRows();
}
catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
/**
* 獲得當(dāng)前頁(yè)數(shù)據(jù),調(diào)用queryPageList()方法,將異常封裝為non-checked異常
* @param startRow 開(kāi)始記錄號(hào)
* @param rowCount 記錄總數(shù)
* @return 當(dāng)前頁(yè)數(shù)據(jù)
* @throws ApplicationRuntimeException
*/
private List pageList(int startRow, int rowCount) throws ApplicationRuntimeException{
try{
return queryPageList(startRow, rowCount);
}catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
//-----------------------------------------
//子類(lèi)實(shí)現(xiàn)的方法
//-----------------------------------------
/**
* 初始化附加方法,由子類(lèi)擴(kuò)展
*/
protected void onInit() {
}
/**
* 查詢獲得總記錄數(shù),由子類(lèi)具體實(shí)現(xiàn)
* @return 總記錄數(shù)
* @throws Exception
*/
protected abstract int queryTotalRows() throws Exception;
/**
* 查詢當(dāng)前頁(yè)數(shù)據(jù),從startRow 開(kāi)始的rowCount條記錄
* @param startRow 開(kāi)始記錄號(hào)
* @param rowCount 記錄總數(shù)
* @return 當(dāng)前頁(yè)數(shù)據(jù)
* @throws Exception
*/
protected abstract List queryPageList(int startRow, int rowCount) throws Exception;
}
3. 分頁(yè)輔助類(lèi)
ViewPageHelper.java/**
* 分頁(yè)輔助類(lèi),用于減化Controller中的代碼
* @author yuanguangdong
* date: Oct 22, 2006
*/
public class ViewPageHelper {
private static final int FIRST_PAGE_VALUE = 1;
private static final int PREVIOUS_PAGE_VALUE = 2;
private static final int NEXT_PAGE_VALUE = 3;
private static final int LAST_PAGE_VALUE = 4;
private static final int SPECIAL_PAGE_VALUE = 5;
public static final String FIRST_PAGE = "FIRST_PAGE";
public static final String PREVIOUS_PAGE = "PREVIOUS_PAGE";
public static final String NEXT_PAGE = "NEXT_PAGE";
public static final String LAST_PAGE = "LAST_PAGE";
public static final String SPECIAL_PAGE = "SPECIAL_PAGE";
/**分頁(yè)動(dòng)作參數(shù)名**/
public static final String PAGE_ACTION = "page_action";
/**分頁(yè)對(duì)象屬性名**/
public static final String SESSION_PAGE = "session_page";
/**頁(yè)號(hào)參數(shù)名**/
public static final String PAGE_NO = "page_no";
private static Map actionMap = new HashMap();
static {
actionMap.put(FIRST_PAGE, new Integer(FIRST_PAGE_VALUE));
actionMap.put(PREVIOUS_PAGE, new Integer(PREVIOUS_PAGE_VALUE));
actionMap.put(NEXT_PAGE, new Integer(NEXT_PAGE_VALUE));
actionMap.put(LAST_PAGE, new Integer(LAST_PAGE_VALUE));
actionMap.put(SPECIAL_PAGE, new Integer(SPECIAL_PAGE_VALUE));
}
/**
* 執(zhí)行分頁(yè)動(dòng)作
* @param page 分頁(yè)對(duì)象
* @param action 分頁(yè)動(dòng)作參數(shù)
* @param pageIndex 頁(yè)號(hào)
*/
public static void doAction(ViewPage page, String action, int pageIndex) {
int actionIndex = 0;
if (page == null) {
throw new NullPointerException("Page對(duì)象null"
;
}
if (action == null || "".equals(action)) {
throw new IllegalArgumentException("無(wú)效的分頁(yè)動(dòng)作參數(shù)null"
;
}
action = action.toUpperCase();
if (!actionMap.containsKey(action)) {
throw new UnsupportedOperationException("不支持的分頁(yè)動(dòng)作參數(shù):" + action);
}
Integer index = (Integer) actionMap.get(action);
actionIndex = index.intValue();
switch (actionIndex) {
case FIRST_PAGE_VALUE:
page.gotoFirstPage();
break;
case PREVIOUS_PAGE_VALUE:
page.gotoPreviousPage();
break;
case NEXT_PAGE_VALUE:
page.gotoNextPage();
break;
case LAST_PAGE_VALUE:
page.gotoLastPage();
break;
case SPECIAL_PAGE_VALUE:
page.gotoPage(pageIndex);
}
}
public static void doAction(ViewPage page, String action){
doAction(page, action, 1);
}
}
五、應(yīng)用通用分頁(yè)框架
1.繼承AbstractViewPage類(lèi),實(shí)現(xiàn)queryPageList(int startRow, int endRow)和
queryTotalRows()方法。
protected int queryTotalRows() throws Exception
獲得查詢條件的總記錄數(shù)
protected List queryPageList(int startRow, int rowCount)
用于查詢指定范圍的數(shù)據(jù)。startRow為開(kāi)始記錄號(hào), rowCount為查詢的記錄數(shù)
queryPageList(0,20)為查詢從第一條開(kāi)始的20條記錄。
使用Ibatis可以由queryPageList調(diào)用queryForList()方法。
/**
* 用戶信息分頁(yè)內(nèi)部類(lèi)
* @author yuanguangdong
* date: Oct 22, 2006
*/
class UserInfoPage extends AbstractViewPage{
//------------------------------------------------
//實(shí)現(xiàn)AbstractViewPage抽象類(lèi)的抽象方法
//------------------------------------------------
/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getPageDate(int, int)
*/
protected List queryPageList(int startRow, int endRow) throws Exception {
return sampleAction.getUserInfoList(startRow, endRow);
}
/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getRows()
*/
protected int queryTotalRows() throws Exception {
return sampleAction.getUserCount();
}
}
3. 在Contrller中的實(shí)現(xiàn)
public ModelAndView listUser(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String pageAction =
RequestUtils.getStringParameter(request,ViewPageHelper.PAGE_ACTION);
Integer pageIndex =
RequestUtils.getIntParameter(request,ViewPageHelper.PAGE_NO);
//聲明分頁(yè)對(duì)象
ViewPage userPage =
(ViewPage) request.getSession().getAttribute(ViewPageHelper.SESSION_PAGE);
//第一次請(qǐng)求
if(pageAction == null || userPage == null){
//構(gòu)建一個(gè)新的分頁(yè)對(duì)象
userPage = new UserInfoPage();
//設(shè)置分頁(yè)大小
userPage.setPageSize(2);
}else{
if(ViewPageHelper.SPECIAL_PAGE.equals(pageAction)){
//如果頁(yè)數(shù)為空,則默認(rèn)為1
if (pageIndex == null)
pageIndex = new Integer(1);
ViewPageHelper.doAction(userPage,pageAction,pageIndex.intValue());
}else{
ViewPageHelper.doAction(userPage,pageAction);
}
}
//從分頁(yè)對(duì)象中獲得當(dāng)前頁(yè)數(shù)據(jù)
List userInfoList = userPage.getPageData();
ModelAndView mav = new ModelAndView(userInfoListView);
mav.addObject(this.userInfoListKey,userInfoList);
request.getSession().setAttribute(ViewPageHelper.SESSION_PAGE,userPage);
return mav;
}
4. jsp頁(yè)面實(shí)現(xiàn)
<%@ page contentType="text/html;charset=utf-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<%@ taglib prefix="tiles" uri="http://jakarta.apache.org/struts/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>顯示所有員工</title>
<SCRIPT language="javaScript">
function pageNoChange(pageNo){
location.href= "ehld.sample.getuserinfolist.do?page_action=SPECIAL_PAGE&page_no="+pageNo.value;
}
</SCRIPT>
</head>
<body>
<table width="80%" border="0">
<tr>
<td bgcolor="#F0FEFF"><div align="left"> 用戶列表</div></td>
</tr>
</table>
<br>
<input name="adduser" type="submit" id="adduser" value="新增用戶" onclick="location.href='ehld.sample.edituserinfo.do'">
<table width="80%" border="0">
<tr bgcolor="#58ED64">
<th width="25%">id</th>
<th width="34%">姓名</th>
<th colspan="2">操作</th>
</tr>
<c:forEach items="${userInfoList}" var="userInfoDTO">
<tr bgcolor="#D6EBF8">
<td><c
ut value="${userInfoDTO.userID}"/></td>
<td><c
ut value="${userInfoDTO.userName}"/></td>
<td width="21%"><a " src="http://www.itpub.net/images/smilies/33.gif" border=0 smilieid="204">ut value='${userInfoDTO.userID}'/>">編輯</a></td>
<td width="20%"><a href="#">刪除</a></td>
</tr>
</c:forEach>
</table>
<c:if test="${session_page.firstPage}">
首頁(yè)
</c:if>
<c:if test="${! session_page.firstPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=FIRST_PAGE">首頁(yè)</a>
</c:if>
<c:if test="${! session_page.hashPreviousPage}">
上一頁(yè)
</c:if>
<c:if test="${session_page.hashPreviousPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=PREVIOUS_PAGE">上一頁(yè)</a>
</c:if>
<c:if test="${!session_page.hashNextPage}">
下一頁(yè)
</c:if>
<c:if test="${session_page.hashNextPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=NEXT_PAGE">下一頁(yè)</a>
</c:if>
<c:if test="${session_page.lastPage}">
尾頁(yè)
</c:if>
<c:if test="${!session_page.lastPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=LAST_PAGE">尾頁(yè)</a>
</c:if>
共有<c
ut value="${session_page.pageCount}" />頁(yè),第
<select name = "pageNo" onChange = "javaScript
ageNoChange(this);">
<c:forEach begin="1" end = "${session_page.pageCount}" var = "pageIndex">
<option " src="http://www.itpub.net/images/smilies/33.gif" border=0 smilieid="204">ut value='${pageIndex}'/>" <c:if test = "${pageIndex ==session_page.pageIndex }">selected</c:if>>
<c
ut value="${pageIndex}"/>
</option>
</c:forEach>
</select>
頁(yè)
</body>
</html>
六、設(shè)計(jì)探討
1.通過(guò)提供queryTotalRows() 和queryPageList(int startRow, int rowCount)方法,交由用戶具體的去實(shí)現(xiàn),所以能夠支持任何數(shù)據(jù)庫(kù)。
對(duì)于Ibatis用戶可以使用queryForList()方法,對(duì)于用jdbc實(shí)現(xiàn)也可以有多種方法來(lái)支持各種數(shù)據(jù)庫(kù)。
Ms sql 可以使用top 關(guān)鍵字,來(lái)獲得指定范圍的數(shù)據(jù)
ORACEL可以使用rowid 偽列來(lái)獲得指定范圍的數(shù)據(jù)
具體怎么去讀取數(shù)據(jù),完全交由用戶控制
2.分頁(yè)對(duì)象與具體的業(yè)務(wù)對(duì)象分離。分頁(yè)對(duì)象如果不能與具體的業(yè)務(wù)對(duì)象分離那么就不可能實(shí)現(xiàn)分頁(yè)對(duì)象的重用,不可以實(shí)現(xiàn)代碼的最大的重用。這不符合oo的按職責(zé)來(lái)設(shè)計(jì)對(duì)象的原則。
3. ViewPageHelper幫助類(lèi)的使用有兩個(gè)好處,統(tǒng)一為分頁(yè)代碼所需的字符參數(shù)進(jìn)行定義,便于contrller和jsp頁(yè)面代碼的維護(hù)。第二便于代碼重用,減少在contrller中的if分支句語(yǔ)。如果不使用幫助類(lèi),則在每個(gè)controller中都會(huì)產(chǎn)生大量相同的代碼。
4. final關(guān)鍵字的使用,protected final void doInit()用于分頁(yè)對(duì)象的實(shí)始化,它讀取并設(shè)置總記錄數(shù),計(jì)算總頁(yè)數(shù),默認(rèn)為第一頁(yè)等。為什么不在構(gòu)造函數(shù)中來(lái)做它呢?如果在構(gòu)造函數(shù)來(lái)做它,子類(lèi)就不可以擴(kuò)展了。像這樣的初始化方法的位置應(yīng)該由擴(kuò)展類(lèi)來(lái)靈活控制。聲明為protected是不讓它由外部對(duì)象來(lái)進(jìn)行訪問(wèn),但是子類(lèi)又可以進(jìn)行調(diào)用。聲明為final是為了子類(lèi)不能重寫(xiě)它,如果子類(lèi)重寫(xiě)不當(dāng)就會(huì)造成分頁(yè)對(duì)象的執(zhí)行邏輯錯(cuò)誤。但是如果子類(lèi)又想擴(kuò)展它怎么辦?子類(lèi)重寫(xiě)protected void onInit()方法就可以了。這樣就能保證父類(lèi)的邏輯,又能夠讓子類(lèi)進(jìn)行擴(kuò)展。
5.異常處理的思考,queryTotalRows()和queryPageList方法都是要求由子類(lèi)實(shí)現(xiàn)的抽象類(lèi),這兩個(gè)類(lèi)的特點(diǎn)都是可能會(huì)調(diào)用業(yè)務(wù)對(duì)象去實(shí)現(xiàn)相應(yīng)的功能,業(yè)務(wù)對(duì)象可能會(huì)訪問(wèn)業(yè)務(wù)數(shù)據(jù)庫(kù)等,可能會(huì)拋出任何Exception,但是分頁(yè)對(duì)象類(lèi)去調(diào)用queryTotalRows()和queryPageList的方法是不應(yīng)該對(duì)這些Exception進(jìn)行任何處理的,如果進(jìn)行try…catch那么就會(huì)隱藏了異常的細(xì)節(jié),這是十分可怕的。如果這些方法拋出異常,分頁(yè)對(duì)象應(yīng)該是不能處理的,不能處理的異常應(yīng)該封裝為運(yùn)行時(shí)異常,所以就有了下面的實(shí)現(xiàn)
private List pageList(int startRow, int rowCount) throws ApplicationRuntimeException{
try{
return queryPageList(startRow, rowCount);
}catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
private int totalRows() throws ApplicationRuntimeException{
try{
return queryTotalRows();
}
catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
分頁(yè)對(duì)象內(nèi)部調(diào)用pageList和totalRows方法,這樣就很好的解決了異常的問(wèn)題,把異常交由外部調(diào)用者去決定是否處理,而不是強(qiáng)制調(diào)用者去處理。
5. 模板方法模式的使用,這是一個(gè)典型的模板方法模式的運(yùn)用。在父類(lèi)實(shí)現(xiàn)關(guān)鍵的算法代碼,實(shí)現(xiàn)分頁(yè)對(duì)象的處理邏輯,而把某些會(huì)發(fā)生改變的方法交由子類(lèi)去實(shí)現(xiàn),使得子類(lèi)完全不用去關(guān)心父類(lèi)的實(shí)現(xiàn)細(xì)節(jié),子類(lèi)只需要重寫(xiě)兩個(gè)簡(jiǎn)單的方法就可以實(shí)現(xiàn)父類(lèi)的功能。這就是模板方法帶來(lái)的最大好處。模板方法模式在各種開(kāi)源框架中有著廣泛的運(yùn)用,看看spring的源碼就知道。子類(lèi)只需要去實(shí)現(xiàn)自己最關(guān)心的細(xì)節(jié),而父類(lèi)實(shí)現(xiàn)那些不變的邏輯或算法。
6. 針對(duì)接口編程,而不是針對(duì)類(lèi)編程。接口可以實(shí)現(xiàn)多重繼承,而類(lèi)卻不能。接口有比類(lèi)獲得更多的好處,更利于擴(kuò)展。比如說(shuō)分頁(yè)接口,它可以讓用戶有更多不同的實(shí)現(xiàn),完全不依賴于任何類(lèi)。只需要為它定制了共同的行為就可以了。在使用委托的時(shí)候接口比抽像類(lèi)更好用。比如在裝飾模式的使用中,可能需要實(shí)現(xiàn)一個(gè)接口,而其中還要有一個(gè)本接口的引用。 如果是抽象類(lèi),則不可以實(shí)現(xiàn)。
7. 通用框架應(yīng)該具有靈活性,不應(yīng)該依懶于任何具體的框架。如果通用框架依懶于某一技術(shù)細(xì)節(jié),某一框架,那么它就有一定的局限性。所以通用分頁(yè)不應(yīng)該依懶于ibatis或hibernate 或spring的某一特點(diǎn)。更不應(yīng)該依懶于sql或oralce某種數(shù)據(jù)庫(kù)。