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

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

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

    每日一得

    不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速開發
    最近關心的內容:SSH,seam,flex,敏捷,TDD
    本站的官方站點是:顛覆軟件

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      220 隨筆 :: 9 文章 :: 421 評論 :: 0 Trackbacks
    keyword:分頁 緩存 eXtremeTable oscache
    引子:這幾天在弄一個關于頁面的分頁,查了一下網上的資料,大都不合要求,要么就是說怎么在數據庫這個層面上如何實現,暈,有了hibernate我用那么費勁翻身么.看到一個用的比較多的方案是做了一個Page工具類,實現諸如getBooks(),getNextPage(),看了一下底層實現居然是"select * from book",嚇死偶了,要是有1千萬條記錄那不是要吐血啊,這哪叫分頁啊,這該叫殺人不見血啊. 一氣之下在jbuilder下建了一個項目就叫FuckPage  :)

    好了,言歸正傳,本文主要說的是關于在展示層一些常用的方案和實現,目錄如下:
    • 手工實現分頁
    • 用eXtremeTable標簽實現自動分頁
    • 用oscache緩存jsp,提高性能
    第一.自己實現一個工具類PageBean完成所有分頁工作.

    本分頁實現概覽:Struts + hibernate
    PageBean負責兩部分內容,一是要在頁面顯示的業務信息,是一個ArrayList;另一個是邏輯控制信息,諸如是否有下一頁,上一頁等等.
    PageBean代碼如下:
    public class PageBean {
      
    int currentPage = 1;//當前頁:Action控制
      int totalPages = 0;//總頁數 :自己運算
      int pageRecorders = 5//每頁記錄數,默認為5,可以在初始化的時候修改//總數據數
      int pageStartRow = 0//每頁的起始數
      int pageEndRow = 0//每頁顯示數據的終止數
      boolean hasNextPage = false//是否有下一頁:自己運算
      boolean hasPreviousPage = false//是否有前一頁 :自己運算
      List objList = new ArrayList();//存放欲展示的對象列表
      int totalRows;//總記錄數,由底層service提供

      
    //是否有上一頁
      public boolean isHasPreviousPage() {
        
    return (currentPage > 1?true:false );
      }

      
    //共有多少頁,service只提供有多少條記錄,多少頁數由PageBean自己運算
      public int getTotalPages() {
        
    return (totalRows/pageRecorders ==0?totalRows/pageRecorders:totalRows/pageRecorders+1);
      }

      
    public int getCurrentPage() {
        
    return currentPage;
      }

      
    public int getPageEndRow() {
        
    return pageEndRow;
      }

      
    //是否有下一頁
      public boolean isHasNextPage() {
        
    return (currentPage < this.getTotalPages() ? true:false);
      }

      
    public int getTotalRows() {
        
    return totalRows;
      }

      
    public int getPageStartRow() {
        
    return pageStartRow;
      }

      
    public int getPageRecorders() {
        
    return pageRecorders;
      }

      
    public void setObjList(List objList) {
        
    this.objList = objList;
      }

      
    public void setHasPreviousPage(boolean hasPreviousPage) {
        
    this.hasPreviousPage = hasPreviousPage;
      }

      
    public void setTotalPages(int totalPages) {
        
    this.totalPages = totalPages;
      }

      
    public void setCurrentPage(int currentPage) {
        
    this.currentPage = currentPage;
      }

      
    public void setPageEndRow(int pageEndRow) {
        
    this.pageEndRow = pageEndRow;
      }

      
    public void setHasNextPage(boolean hasNextPage) {
        
    this.hasNextPage = hasNextPage;
      }

      
    public void setTotalRows(int totalRows) {
        
    this.totalRows = totalRows;
      }

      
    public void setPageStartRow(int pageStartRow) {
        
    this.pageStartRow = pageStartRow;
      }

      
    public void setPageRecorders(int pageRecorders) {
        
    this.pageRecorders = pageRecorders;
      }

      
    public List getObjList() {
        
    return objList;
      }

      
    public PageBean() {}


      
    public void description() {

        String description 
    = "共有數據數:" + this.getTotalRows() +

            
    "共有頁數: " + this.getTotalPages() +

            
    "當前頁數為:" + this.getCurrentPage() +

            
    " 是否有前一頁: " + this.isHasPreviousPage() +

            
    " 是否有下一頁:" + this.isHasNextPage() +

            
    " 開始行數:" + this.getPageStartRow() +

            
    " 終止行數:" + this.getPageEndRow();

        System.out.println(description);
      }
    }

    注意,我沒有在PageBean里放具體的業務邏輯,諸如getBooks()等,目的很簡單,具有通用性,業務邏輯由另一個業務類實現BookService,BookService獲得的業務數據都放在了PageBean的ArrayList里.

    BookService代碼如下:
    public class BookService {
      
    private static Logger log = Logger.getLogger(BookService.class.getName());
      
    public BookService() {
      }

      
    /**
       * 獲得book列表
       * 
    @param pageBean PageBean:返回的對象存在pageBean里
       
    */
      
    public static void getBooks(PageBean pageBean) {
        String infoSql 
    = "from Book";//獲得業務信息
        String countSql = "select count(*) from Book";//獲得控制信息
        Session session = null;
        
    try {
          session 
    = DBUtil.currentSession();
          Query query 
    = session.createQuery(infoSql);
          query.setFirstResult((pageBean.getCurrentPage()
    -1)* pageBean.getPageRecorders());//起始頁
          query.setMaxResults(pageBean.getPageRecorders());//每頁記錄數
          pageBean.getObjList().clear();
          
    for (Iterator it = query.iterate(); it.hasNext(); ) {
            Book po 
    = (Book)it.next();
            BookVo vo 
    = new BookVo();
            BeanUtils.copyProperties(vo,po);
            pageBean.getObjList().add(vo);
          }
          session 
    = DBUtil.currentSession();
          query 
    = session.createQuery(countSql);
          
    int totalRecords = ((Integer)query.list().get(0)).intValue();
          pageBean.setTotalRows(totalRecords);
        }
        
    catch (Exception e) {
          e.printStackTrace();
          System.out.println(
    "數據庫異常" + e.toString());
        }
        
    finally {
          
    try {
            
    if (null != session) {
              session.close();
            }
          }
          
    catch (HibernateException ex) {
            ex.printStackTrace();
          }
        }

      }


    }

    在Struts的Action中調用service,返回一個PageBean給展示頁面
    Action代碼如下:
     1 public class PageListAction extends Action {
     2 
     3   public PageListAction() {}
     4 
     5   ArrayList arrayList = new ArrayList();
     6 
     7   public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response) throws Exception {
     8     String action;
     9     PageBean pageBean = null;
    10     action = request.getParameter("action");
    11     if(action == null || action.equals("null")) { //第一次讀取數據
    12       pageBean = new PageBean();
    13     } else {//用戶選擇上一頁或者下一頁
    14       if(action == "nextPage" || action.equals("nextPage")) {
    15         pageBean = (PageBean)request.getSession().getAttribute("pageBean");
    16         pageBean.setCurrentPage(pageBean.getCurrentPage()+1);
    17       }else if(action == "previousPage" || action.equals("previousPage")) {
    18         pageBean = (PageBean)request.getSession().getAttribute("pageBean");
    19         pageBean.setCurrentPage(pageBean.getCurrentPage()-1);
    20       }else if(action == "targetPage" || action.equals("targetPage")){//指定頁
    21         pageBean = (PageBean)request.getSession().getAttribute("pageBean");
    22         System.out.println("targetPage=" + request.getParameter("targetPage"));
    23         //這里根據需要可以對填寫的目標頁進行判斷,不要大于最大頁數[此處省略]
    24         pageBean.setCurrentPage(Integer.parseInt(request.getParameter("targetPage")));
    25       }
    26     }
    27     if(null == pageBean) throw new Exception("獲得PageBean異常");
    28     BookService service = new BookService();
    29     service.getBooks(pageBean);
    30     pageBean.description();
    31     request.getSession().setAttribute("pageBean",pageBean);
    32     request.setAttribute("result",pageBean.getObjList());
    33     return(mapping.findForward("success"));
    34   }
    35 }

    在本Action中判斷了可能出現的三種情況:
    1. 用戶選擇了"上一頁"
    2. 用戶選擇了"下一頁"
    3. 用戶手工輸入了指定的某一頁
    這里有點感覺不爽的是必須hard coding,但是不這么做感覺暫時也想不出什么好的辦法來,畢竟一個PageBean不可能封裝所有的細節,如果你有更好的方式請指點哦 :)

    好了,到了我們呼之欲出的展示頁面了 :)
    show.jsp代碼如下
    <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
    <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
    <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
    <%@ page contentType="text/html; charset=gb2312" language="java"%>

    <html:html locale="true">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
      
    <script language="javaScript">
        
    function go(){
          
    try{
          
    var targetValue = document.getElementById("targetPage").value;
          parseInt(targetValue);
          alert(targetValue);
          }
    catch(e){
            alert(
    "請正確填寫目標頁");
            
    return;
          }
          
    if(targetValue == null || targetValue == ''){
            alert(
    "請填寫目標頁");
            
    return;
          }
          window.location 
    = "/fuck/pageList.do?action=targetPage&targetPage="+targetValue;
        }
      
    </script>
    </head>
    <body>
    <logic:present name="pageBean">
      共有數據總數
    <bean:write name="pageBean" property="totalRows"/>;
    共分
    <bean:write name="pageBean" property="totalPages"/>頁,
    當前是第
    <bean:write name="pageBean" property="currentPage"/>
    </logic:present>
    <table border="1">
    <tr><th>書名</th><th>作者</th><th>價格</th></tr>
        
    <logic:present name="result">
            
    <logic:iterate id="book" name="result">
            
    <logic:present name="book">
            
    <tr>
               
    <td><bean:write name="book" property="name" /></td>
               
    <td> <bean:write name="book" property="author" /></td>
               
    <td><bean:write name="book" property="price" /></td>
            
    /tr>
            
    </logic:present>
            
    </logic:iterate>
        
    </logic:present>
    </table>
    <logic:present name="pageBean">
    <logic:equal name="pageBean" property="hasNextPage" value="true">
        
    <html:link page="/pageList.do?action=nextPage">nextPage</html:link>
    </logic:equal>
    <logic:equal name="pageBean" property="hasPreviousPage" value="true">
        
    <html:link page="/pageList.do?action=previousPage">PreviousPage</html:link>
    </logic:equal>
    <input type="text" name="targetPage" id="targetPage"/>
    <input type="button" value="go!" size="2" onclick="go();"/>
    </logic:present>
    </body>
    </html:html>

    是否有上一頁或者下一頁,全部根據PageBean里的邏輯值動態判斷.
    這個頁面沒什么可說的,你可以根據自己的情況調整就OK了


    第二. eXtremeTable標簽實現自動分

    上面的方案大家已經看出來了,實際上是每一次用戶點擊一個頁面都會查詢數據庫,這可以算是既是優點也是缺點,優點是數據庫不用一次查詢出所有的數據,在高數據量的情況下尤其如此,缺點就是和數據庫的交互次數有點多了,不過這個完全看你的業務策略了,如果用戶大多數情況下就是看沒幾條的記錄,你又何必把全部數據給他取出來呢? 當然,在這里我們就說說一次取出全部數據,然后讓標簽幫助我們自動分頁,終于可以偷懶了,你所要做的僅僅是取出所需要的業務數據而已,其他的就交給eXtremeTable標簽來完成就OK.
    eXtremeTable
    標簽的下載,安裝和文檔請參看官方網站

    public static List getBooks() {
        log.debug(
    "execute getBooks method!");
        String infoSql 
    = "from Book";//獲得業務信息
        Session session = null;
        List rtnList 
    = new ArrayList();
        
    try {
          session 
    = DBUtil.currentSession();
          Query query 
    = session.createQuery(infoSql);
          
    for (Iterator it = query.iterate(); it.hasNext(); ) {
            Book po 
    = (Book) it.next();
            BookVo vo 
    = new BookVo();
            BeanUtils.copyProperties(vo,po);
            log.debug(
    "vo = [" + vo + "]");
            rtnList.add(vo);
          }
        }
        
    catch (Exception e) {
          e.printStackTrace();
          System.out.println(
    "數據庫異常" + e.toString());
        }
        
    finally {
          
    try {
            
    if (null != session) {
              session.close();
            }
          }
          
    catch (HibernateException ex) {
            ex.printStackTrace();
          }
        }
        
    return rtnList;
      }
    • Action
    代碼如下:
    public class PageListWithTagAction extends Action {
      
    public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        BookService service 
    = new BookService();
       
        //我這里把數據放到session中,相當于做了緩存,根據你的業務策略也可以不用這么做
        
    //如果session中沒有,則從數據庫中查詢
        if(null == httpServletRequest.getSession().getAttribute("result")){
        List result 
    = service.getBooks();
        httpServletRequest.getSession().setAttribute(
    "result",result);
      }
        
    return (actionMapping.findForward("success"));
      }
    }

    • jsp[show.jsp代碼如下,留心里面的標簽使用方法]
    <body bgcolor="#ffffff">
    <ec:table tableId="fuck"
            items
    ="result"
            action
    ="${pageContext.request.contextPath}/pageListWithTag.do"
            imagePath
    ="${pageContext.request.contextPath}/jsp/images/table/*.gif"
            title
    ="Books"
            width
    ="60%"
            rowsDisplayed
    ="5"
            locale
    ="zh_CN"
            cellpadding
    ="1"
            cellspacing
    ="1"
            border
    ="1"
            method
    ="post"
            showPagination
    ="false"
            filterable
    ="false"
            
    >
           
    <ec:exportXls fileName="Book.xls" tooltip="導出Excel">
           
    </ec:exportXls>
           
    <ec:exportPdf fileName="Book.pdf" tooltip="導出pdf" headerColor="blue" headerBackgroundColor="red" headerTitle="Book"></ec:exportPdf>
            
    <ec:row highlightRow="true">
                
    <ec:column property="name" title="書名">
                              
    <href="${pageContext.request.contextPath}/bookDetail.do?bookID=${fuck.id}&amp;bookName=${fuck.name}">${fuck.name}
                              
    </a>
                            
    </ec:column>
                
    <ec:column property="author" title="作者">
                              
    <!--here can't use 'result.author'-->
                              ${fuck.author}
                            
    </ec:column>

                
    <ec:column property="price" title="價格" cell="currency" format="$###,###,##0.00"/>
                            
    <ec:column property="date" title="日期" cell="date" format="yyyy年MM月dd日">
                            
    </ec:column>
            
    </ec:row>
        
    </ec:table>
    </body>

    感覺如何?你不用再畫你的頁面了,不用再畫table了,這點我特別喜歡,因為我自己畫的東西都比較難看,畢竟我的美工功夫不夠  :(   "自從有了eXtremeTable吃嘛嘛香"    :)
    具體的標簽使用方法請參考官方文檔的Manual,說明還是比較詳細的.

    第三.用oscache緩存你的頁面

    為了提高頁面的速度我們想了很多辦法,比如預編譯的辦法,以及把你常用的數據放到內存里,是的,除了用內存我們還能想到用什么辦法呢,恩,我想以后cpu的緩存也特別大的話我們的下一個方案肯定就是把數據全部放到cpu里得了,哈哈,展望一下  :)

    言歸正傳,oscache的廣告我就不做了,差不多地球人都知道了,這里僅僅提供了一個想法,我想這確實是一個不錯的方案,它提供了2個途徑使用,一是通過tag的方式,可能也是用的最多的方式,另一個便是調用API,當然就可以在任何想調用的地方使用了.另外還有一個特別不錯的功能就是有策略的刷新數據.這是一個useful的方式,比如你做了增刪改操作那么數據庫的數據已經發生變化了,你可以通知緩存來更新數據,方式是通過key或者group.
    下面是幾個摘自FAQ里的幾個常用Example
    • Example1
    <cache:cache time="600">
            
    <%= myBean.getTitle() %>
     
    </cache:cache>

    • Example2
    <cache:cache key="foobar" scope="session">
            
    <%= myBean.getTitle() %>
    </cache:cache>

    • Example3
    <cache:cache>
            
    <% try { %>
                
    <%= myBean.getTitle() %>>
            
    <% } catch (Exception e) { %>
                
    <% application.log("Exception occurred in myBean.getTitle(): " + e); %>
                
    <cache:usecached />
            
    <% } %>
    </cache:cache>

    上面的Example3可以實現在數據庫當機的情況下從緩存里讀取數據展示,顯得更加友好

    不知你注意到了沒有,在oscache官方展示的例子里的jsp都有一個我稱之為毛病的東西,或者說是困惑,那就是都用了
    <%=.%>
    這種方式,感覺有點別扭,畢竟這種使用方式對于別人我不知道,反正對于我來說用的比較少,在以前的使用中我記得只有使用xml數據島的時候用過這種方式,其他情況下很少用=的方式來打印出一些動態的數據,更常見的可能是如下這樣的情況:
    <cache:cache key="dispInfo" groups="disInfo" time="1200">
      
    <%
      BookService service 
    = new BookService();
      List list 
    = service.getBooks();
      request.setAttribute(
    "list",list);
      
    %>
      
    </cache:cache>
     
    <c:forEach items="${list}" var="item">
      
    <c:out value="${item.id}"/> == <c:out value="${item.name}"/><br />
    </c:forEach>

    不幸的是,這個一廂情愿的做法并不被oscache支持,除了第一次能夠顯示數據,下一次就顯示不了了.也許oscache所謂的緩存jsp代碼就是指緩存諸如<%=%>的方式才是jsp代碼,其他的java型的就不被支持了?可能是理解不夠,繼續研究吧,希望有知道的不吝賜教 :)

    恩,就是這么多,關于頁面的緩存和分頁以及標簽.歡迎有這方面的更好的想法來交流.
    本人不才,可能有很多地方理解不夠深入,見笑了  :)




    posted on 2006-02-25 21:26 Alex 閱讀(2680) 評論(6)  編輯  收藏 所屬分類: java

    評論

    # re: Fuck the page! 關于分頁,標簽,緩存 2006-02-26 10:45 JAVA夢想
    關于eXtremeTable:
    我已經得到作者(Jeff)同意并翻譯了官方的使用指南(共七篇),發布在我的blog上。歡迎大家參閱,指正。我現在正在翻譯它的參考手冊。  回復  更多評論
      

    # re: Fuck the page! 關于分頁,標簽,緩存 2006-02-26 13:10 Alex
    正在想關于翻譯的事情要不要弄一下呢,替大伙謝謝你了  回復  更多評論
      

    # re: Fuck the page! 關于分頁,標簽,緩存 2006-02-28 14:23 胡子魚
    呵,第一種分頁,封裝不夠徹底;第二種分頁只能小數據量,所以建議你改進第一種封裝,把分頁邏輯和具體業務分開,這樣,每個業務,對同一個getPageBean就完成分頁了。  回復  更多評論
      

    # re: Fuck the page! 關于分頁,標簽,緩存 2006-03-29 10:37 keke
    標簽太多.不爽.一個字:亂  回復  更多評論
      

    # re: Fuck the page! 關于分頁,標簽,緩存 2006-03-29 12:02 keke
    如果resultset中有1000000條,這種分頁好恐怖.  回復  更多評論
      

    # re: Fuck the page! 關于分頁,標簽,緩存 2006-03-29 12:04 keke
    "如果resultset中有1000000條,這種分頁好恐怖." 看錯了.這個可以控制.呵呵  回復  更多評論
      

    主站蜘蛛池模板: 日韩a级毛片免费视频| 亚洲精品第一综合99久久| 日韩激情无码免费毛片| 最近免费中文字幕大全免费 | jyzzjyzz国产免费观看| 亚洲va在线va天堂va手机| 亚洲国产成人久久综合一| 亚洲综合区小说区激情区| 国产精品自在自线免费观看| 99在线视频免费观看视频| 热re99久久6国产精品免费| 国产在线观看xxxx免费| 产传媒61国产免费| 黄页免费视频播放在线播放| 亚洲精品美女网站| 国产亚洲国产bv网站在线| 亚洲天堂一区在线| 亚洲特级aaaaaa毛片| 久久亚洲日韩精品一区二区三区| 亚洲精品卡2卡3卡4卡5卡区| 久99精品视频在线观看婷亚洲片国产一区一级在线 | 一级毛片免费毛片一级毛片免费| 国产精品免费久久久久影院 | 亚洲精品视频在线观看你懂的| 成人免费视频国产| 日日夜夜精品免费视频| 午夜电影免费观看| 免费无码一区二区三区蜜桃大 | 久久精品国产亚洲AV| 亚洲av无码专区国产不乱码| 亚洲综合小说另类图片动图 | 久久精品免费全国观看国产| 18以下岁毛片在免费播放| 69pao强力打造免费高清| free哆啪啪免费永久| 在线天堂免费观看.WWW| 成人免费毛片内射美女APP| 久久久www成人免费毛片| 青草草在线视频永久免费| 国产成人免费一区二区三区| yy6080亚洲一级理论|