Fuck the page! 关于分页,标签,缓存

 本文主要说的是关于在展示层一些常用的方案和实现,目录如下:
  手工实现分页
  • 用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型的就不被支持了?可能是理解不够,继续研究吧,希望有知道的不吝赐教 :)

恩,就是这么多,关于页面的缓存和分页以及标签.欢迎有这方面的更好的想法来交流.
本人不才,可能有很多地方理解不够深入,见笑了  :)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值