分頁(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><cut value="${userInfoDTO.userID}"/></td>
    <td><cut 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>

共有<cut value="${session_page.pageCount}" />頁(yè),第

<select name = "pageNo" onChange = "javaScriptageNoChange(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>>
              <cut 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ù)。