<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-18  評論-8  文章-0  trackbacks-0

          分頁是一種最簡單且廣泛使用的方法,可以把大數(shù)據(jù)集分成小的數(shù)據(jù)塊。它是Web站點設(shè)計的一個核心部分,包括UI,即客戶端(管理屏幕顯示的內(nèi)容)和服務(wù)器端(高效處理大結(jié)果集,防止資源的大量消耗及服務(wù)的時延)。

    分頁機(jī)制需要基于以下兩個條件:
    1.) 屏幕顯示的信息是受限的。
    2.) 資源配置-數(shù)據(jù)庫連接數(shù)和內(nèi)存的使用量。

    分頁機(jī)制有兩種方式:基于緩存的和基于查詢的。
    基于緩存:把數(shù)據(jù)庫中查詢的結(jié)果存儲起來,可以是HTTP Session、Stateful Session Bean或自定義的一個緩存機(jī)制。下次請求產(chǎn)生的頁面數(shù)據(jù)就從緩存中而不是數(shù)據(jù)庫中取得,對于少量的數(shù)據(jù)和重復(fù)的查詢,將非常有用。缺點就是當(dāng)結(jié)果集很大時,會占用大量的內(nèi)存,長時間的進(jìn)行查詢,進(jìn)而連接超時,長時間占據(jù)數(shù)據(jù)庫連接和session資源。
    基于查詢:不使用緩存,直接從數(shù)據(jù)庫中取得所需的數(shù)據(jù),但請求的響應(yīng)時間將變長。
    所以,大多數(shù)情況是混合使用兩種方法,設(shè)定緩存的大小,維護(hù)少量的緩存數(shù)據(jù),使用數(shù)據(jù)連接池,利用高效的查詢得到數(shù)據(jù)。

    基于查詢的分頁策略
    關(guān)鍵是只從數(shù)據(jù)庫取得是頁面所需的數(shù)據(jù)。最簡單的方法是把所以的結(jié)果都取出來,在迭代輸出到頁面,或利用JDBC取得特定的行,例如JDBC的ResultSet::absolute()。
    要注意的就是,雖然在一段連續(xù)的數(shù)據(jù)中某些多余的數(shù)據(jù)是不需要的,但DBMS必須在一個臨時區(qū)域存放這個集合,為了能讓游標(biāo)移動到指定的開始行。避免這種情況可以使用WHERE從句,把你不需要的數(shù)據(jù)明確的排除,這就要求你從前\后頁的請求中附帶某些信息:
        * 頁面指向(Paging direction)-前進(jìn)、后退
        * 查詢條件(Search criteria)
        * 目標(biāo)頁面(The target page)-只對于請求頁面返回需要的行
        * 頁面大小(Page size)-每頁顯示多少行

    頁面指向指可以向前或向后訪問結(jié)果數(shù)據(jù)。緩存能提供向后的功能,把已經(jīng)得到的數(shù)據(jù)存儲起來,但它只針對于靜態(tài)數(shù)據(jù)。如果你的數(shù)據(jù)是動態(tài)的,那你緩存中可能沒有先前頁面的數(shù)據(jù),只能用另外的查詢?nèi)ト〉昧恕τ诓皇褂镁彺娴姆猪摚玫絆RDER BY和WHERE 從句,對一行或多行使用ORDER BY產(chǎn)生向前(ASC)或向后(DESC)的功能。
    通常一個頁面由一對開始和結(jié)束的row-id構(gòu)成。row-id可以是一個主鍵,也可能是由主鍵和其它的列組成。對于下一頁,調(diào)整查詢語句取得所有row-id比當(dāng)前頁的結(jié)束row-id大的行;對于上一頁,取得所有row-id比當(dāng)前頁的開始row-id小的行。這對于單列的查詢能完成的很好,但在大多數(shù)情緒下沒這么簡單,row-id由多列構(gòu)成,混合了ACS和DESC ORDER BY從句。所以這就要在WHERE從句中包含所有的ORDER BY的列,把所有的row-id的列包含在ORDER BY中,要改變的只是ORDER的順序。

    基于row-id的分頁方式
    舉一個最簡單的查詢例子:foo表有三列,col1 (VARCHAR)、col2 (INTEGER)和col3 (TIMESTAMP)。
    得到第一頁數(shù)據(jù)的SQL語句:SELECT col1,col2,col3 FROM  foo WHERE col1=? ORDER BY col2 ASC ,col3 DESC;
    在這個例子中,查許條件由用戶提供給col1,row-id由col2和col3構(gòu)成,分頁由col2升序和col3降序來控制。
    JDBC給我們提供了一個限制查詢行數(shù)的方法:Statement::setMaxRows(int max),一個性能上更好的方法是:Statement::setFetchSize(int rows)。
    得到下一頁的SQL語句:SELECT col1,col2,col3 FROM  foo WHERE col1=? AND ( (col2 >  ?  )  OR  (col2 = ?  AND col3 <  ?  )  )  ORDER BY  col2 ASC ,col3 DESC;
    得到前一頁的SQL語句:SELECT col1,col2,col3 FROM  foo WHERE   col1=?  AND ( (col2 <  ?  )  OR  (col2 = ?  AND col3 >  ?  )  )  ORDER BY  col2 DESC ,col3 ASC;

    基于查詢的實踐
    一旦確定使用基于查詢的分頁機(jī)制,就開始動手創(chuàng)建一個通用的、可復(fù)用的分頁組件,以應(yīng)付各種類型的查詢。這就要求提供一個清晰的接口,一個簡單API,封裝底層的分頁算法的分頁框架(framework)。它可以處理數(shù)據(jù)的取得、傳輸、row-id和差數(shù)的傳遞、查詢操作、查詢條件差數(shù)的替換。
    建立這個框架可以采用配置驅(qū)動,在一個配置文件中預(yù)先定義好各種信息,例如 row-id, ORDER BY列。執(zhí)行查詢時,調(diào)用者是不需要涉及SQL語法的,只要定義一個你所需的view。
    view是一個聯(lián)系數(shù)據(jù)庫表和視圖載體。一個page view包含分頁所需的信息(像row-id)。在.properties文件中配置page view,定義頁面的指向(ORDER BY)和row-id。
    下面的步驟是創(chuàng)建一個分頁組件的過程,所有的源代碼點擊這里下載。

    第一步.創(chuàng)建/配置你的page view
    # Example view
    example.view=foo
    example.pagesize=5
    example.where=col1=?
    example.rowids=col2 ASC,col3 DESC

    第二步.創(chuàng)建一個類,去表述你的view-PageDefn
    你可以從配置文件中裝載它,或自己創(chuàng)建它。配置文件當(dāng)然是首選,調(diào)用者不需要知道view的內(nèi)部構(gòu)造或表結(jié)構(gòu)。你的PageInfn包含了所有產(chǎn)生SQL語句的細(xì)節(jié):

    public class PageDefn extends ViewDefn {

     
    public interface PageAction {
         
    public static final int FIRST = 0;
         
    public static final int NEXT = 1;
         
    public static final int PREVIOUS = 2;
         
    public static final int CURRENT = 3;
       }


        
    protected int pageSize;
        
    protected ColumnDesc[] rowIds;

     
    public PageDefn() {
          super();
          rowIds 
    = null;
          pageSize 
    = 50;
        }


     
    public PageDefn(String viewname) {
          super(viewname);
          rowIds 
    = null;
          pageSize 
    = 50;
       }

     
    }

    第三步.創(chuàng)建一個DAO(date access object)
    創(chuàng)建一個起特殊用途PagingDAO,支持參數(shù)替代和PerparedStatement。Prepared statements比一般的查詢速度更快,因為它能被DBMS預(yù)編譯。你的PagingDAO執(zhí)行分頁查詢和結(jié)果的處理,實際的SQL構(gòu)造交給了PageDefn。這種分離的關(guān)系允許你對PageDefn進(jìn)行擴(kuò)展,它的子類可以支持更多優(yōu)秀的查詢構(gòu)造而不依賴DAO。
    當(dāng)查詢執(zhí)行結(jié)束,確認(rèn)你的DAO關(guān)閉了所有的數(shù)據(jù)庫資源(ResultSets、Statements和Connections)。把你的關(guān)閉代碼放到FINALLY子句中,確保有異常拋出是也可以關(guān)閉資源。

    第四步.創(chuàng)建PageContext和PageCommand
    PageCommand類可以封裝你的頁面請求和放置結(jié)果,客戶端servlet和action會使用PageCommand作用于framework,但必須提供如下方法:
        * 指定目標(biāo)view(PageDefn)
        * 提供可選的查詢條件(query parameters) 
        * 指定頁面action(FIRST, CURRENT, NEXT, or BACK) 
        * 訪問頁面結(jié)果

    另外,你的PageCommand應(yīng)該封裝所有實現(xiàn)分頁所需的請求信息。創(chuàng)建一個context對象放置請求action和結(jié)果,并封裝了分頁信息:

    public class PageContext implements Serializable {

     
    protected Object page; // results
     protected Object firstEntry; //  first row ID 
     protected Object lastEntry; // last row id
     protected int action; // paging action
     protected PageContext prevContext; // previous context state

     
    public PageContext() {
      
    this.page = new Object[0];
      
    this.firstEntry = null;
      
    this.lastEntry = null;
      
    this.action = PageDefn.PageAction.FIRST;
     }


     
    public Object[] getPage() {
      
    return ((Collection) page).toArray();
     }


     
    public void setPage(Object page) {
      
    this.page = page;
     }


     
    public int getAction() {
      
    return action;
     }


     
    public void setAction(int action) {
      
    this.action = action;
     }

    }

    確保你的PageContext對象是可序列化的,它可用于傳輸。創(chuàng)建PageCommand封裝你的頁面請求,并把PageContext作為一個成員變量:

    public class PageCommand extends ViewCommand {

        
    protected PageContext context;

    }

    PageCommand是一個DTO(data transfer object),用來傳遞請求參數(shù)和頁面結(jié)果,頁面的數(shù)據(jù)和row-id被放在PageContext對象中,調(diào)用者就不需要知道PageContext的屬性了,直接通過頁面請求讀取就可以了。 

    第五步.創(chuàng)建一個PagingService
    創(chuàng)建一個專門的分頁service(更多COR services 和configuration相關(guān)的看The COR Pattern Puts Your J2EE Development on the Fast Track )處理PageCommond請求,并傳遞給PagingDAO:

    public class PageService implements Service {

     
    public Command process(Command command) throws ServiceException {
      
    if (!command.getClass().equals(PageCommand.class)) {
       
    return null;
      }


      PageCommand pcmd 
    = (PageCommand) command;

      PageContext context 
    = pcmd.getContext();
      PageDefn desc 
    = (PageDefn) pcmd.getDefn();

      
    // Get A DAO    
      PagingDAO dao = PagingDAO.get();
      
    // Issue Query, results are set into the context by the DAO
      try {

       dao.executeQuery(desc, context, 
    true);
       
    return pcmd;
      }
     catch (DAOException e) {
       
    throw new ServiceException(e);
      }
     }
     }

    第六步.實現(xiàn)你的PageCommand
    為了取得第一頁,創(chuàng)建一個PageCommand實例,裝載你的目標(biāo)view,設(shè)置所有用戶提供的參數(shù)和頁面action,傳遞給CORManager。CORManager將相應(yīng)的信息交給PageService 處理:

    // load view and set user supplied criteria
    PageDefn d = PageDefnFactory.getPDF().createPageDefn(bundle,view);
    d.setParams(
    new Object[] { criteria });
    PageCommand cmd 
    = new PageCommand(d);

    // fetch the first page
    cmd.setAction(PageDefn.PageAction.FIRST);
    cmd 
    = (PageCommand) CORManager.get().process(cmd);

    // process results
    PageContext context = cmd.getContext();

    // Process results
    Object[] rows = cmd.getContext().getPage();
    if (rows == nullreturn;
    for (int i = 0; i < rows.length; ++i) {
     System.
    out.println("ROW(" + i + "" + rows[i].toString());
    }


    // cache context to be reused for subsequent pages..
    getServletContext().setAttribute("context",cmd.getContext());

    在得到下一頁面前,確保在先前的請求中復(fù)用PageContext ,它包含所有分頁所需的信息。如果你用servlet管理分頁,在HttpSession中緩存PageCommand和PageContext對象,這樣你就可以在以后的頁面中重復(fù)使用:

    // Create PageDefintion
    ..
    PageDefn d 
    = PageDefnFactory.getPDF().createPageDefn(bundle, view);

    // Retrieve context from ServletContext
    PageContext c = (PageContext) getServletContext().getAttribute("context");

    // Create Page Command  
    PageCommand cmd = new PageCommand(d,c);
    cmd.setAction(PageDefn.PageAction.NEXT);
    cmd 
    = (PageCommand) CORManager.get().process(cmd);

    // cache result on servlet context
    getServletContext().setAttribute("context",cmd.getContext());

    當(dāng)前頁數(shù)和總共頁數(shù)
    你也許想知道當(dāng)前頁面的頁碼和總共的頁數(shù)。你可以把這些信息顯示給用戶看,防止超過頁碼的范圍。對于基于查詢的分頁,最簡單的方法是SQL的COUNT(*)或COUNT(column name)。 你能夠通過劃分每頁的大小來確定總共的頁數(shù)。當(dāng)然你不需要每次查詢的時候都執(zhí)行COUNT(),在取得第一頁的數(shù)據(jù)時執(zhí)行一次就可以了,這也許會導(dǎo)致首頁的顯示變慢。當(dāng)如果數(shù)據(jù)變化的很大時,這個步驟是不需要的。
    為了增加頁碼和總共的頁數(shù),需要擴(kuò)展PageContext對象去放置數(shù)據(jù),也要擴(kuò)展PagingDAO,在首頁的請求時,執(zhí)行一次count()的查詢 。

    結(jié)語
    絕大多數(shù)Web站點需要查詢所有的數(shù)據(jù)。分頁是約束服務(wù)器資源、大量數(shù)據(jù)查詢、數(shù)據(jù)在頁面的顯示過多的一種方法。根據(jù)需求和數(shù)據(jù)量選擇基于緩存的、基于查詢的或混合型分頁機(jī)制,可以有效的提高性能,防止內(nèi)存的過量銷毀以及數(shù)據(jù)庫連接長時間的占用。

    原文:Paging in J2EE: Manage Large Result Sets Efficiently 

    posted on 2005-02-22 22:33 阿姆斯壯 閱讀(1960) 評論(3)  編輯  收藏 所屬分類: 基礎(chǔ)很重要

    評論:
    # re: J2EE中的分頁(翻譯) 2005-03-09 14:17 | simpson
    good  回復(fù)  更多評論
      
    # re: J2EE中的分頁(翻譯) 2006-06-24 09:50 | wu
    支持  回復(fù)  更多評論
      
    # re: J2EE中的分頁(翻譯) 2006-12-04 13:24 | 壞男孩
    主站蜘蛛池模板: 67pao强力打造67194在线午夜亚洲| 亚洲永久在线观看| h片在线免费观看| 亚洲av无码片区一区二区三区| 国产精品无码素人福利免费| 久久免费99精品国产自在现线 | 一个人看的免费视频www在线高清动漫| 国产亚洲视频在线播放| 黄色片在线免费观看| 国产无限免费观看黄网站| 亚洲成人一级电影| 亚洲伊人成无码综合网 | 黄色视屏在线免费播放| 亚洲一区精品视频在线| 亚洲日韩v无码中文字幕| 成人无遮挡毛片免费看| 女人体1963午夜免费视频| 国内成人精品亚洲日本语音| 91亚洲va在线天线va天堂va国产| 无码国产亚洲日韩国精品视频一区二区三区 | 成人国产网站v片免费观看| 亚洲最大中文字幕| 亚洲熟妇少妇任你躁在线观看无码 | 亚洲高清偷拍一区二区三区| 亚洲高清免费在线观看| 成人无码区免费A∨直播| 亚洲av无码一区二区三区天堂 | 亚洲AV色吊丝无码| 久久精品亚洲中文字幕无码网站 | 日韩精品视频免费在线观看| 日本黄网站动漫视频免费| 特级做A爰片毛片免费看无码 | 国产亚洲精品成人AA片新蒲金| 免费的一级黄色片| 18禁男女爽爽爽午夜网站免费| 两个人日本免费完整版在线观看1| 亚洲码欧美码一区二区三区| 亚洲毛片无码专区亚洲乱| 久久青青成人亚洲精品| 伊人婷婷综合缴情亚洲五月| 男人的天堂亚洲一区二区三区|