关于jsp中的分页技术

    欢迎转载,转载时注明来自彭仁夔Blog(http://blog.csdn.net/jxnuprk0924/)

  1、概述

       提起分页技术,基本上可以这样说,每个web系统都会用到。但什么是分页技术?

   下面给出一些我经常光顾网站的的分页截图:

 

   这个是www.zhaopin.com的分页的截图。当你点[10]时,你会得到:

 

 看到什么不同了吗?

 再给一个www.mycodes.net的一个分页的截图。

 在这个例子中,你能发现什么?每个网站或b/s系统都应该有着各种不同的风格。也就是它们的分页的样式各不一样。但他们又有什么相同的地方?那么能不能把它们相同的东西给抽提出来?

 在网上搜了一些有关于这方面的资料。没有发现自己满意的。有很多的给出的例子与具体的数据耦合。其实我们要的是仅仅是在页面上用标签去完成一个分页的功能,而不要去设定具体的数据。也就是从后台的数据分离出来。

    当然,国外开源一些相关的列表标签也含有分页功能。如displaytagdotj等。但是有很多时,你在开发系统时会发现用他们的的标签对于列表来说是不适合的。非要自己去实现循环,去控制数据的显示。而且那么标签的分页对于我们中国人的Web系统也是不是很合适的。也就是说不能完全适合于我们的要求(题外话,displaytag真的很好用。还有报表功能)。

  国内也有一些人开发了分页的标签,如jpagerpagercontrol.它们都是单纯用来完成分页的功能。把循环列表的实现分离出来。这样,我们还可以用 jstl中的 <c:foreach>标签随心所欲地实现具体的列表功能。当然也可以用到displaytag之类的。

 

二:常用的分页技术

如果你还处在刀耕火种的的年代,你一般会用到如下的相似的分页:

  <%int pages=1;   

String mesg = "";

if (request.getParameter("page")!=null && !request.getParameter("page").equals("")) {

       String requestpage = request.getParameter("page");    

       try {

              pages = Integer.parseInt(requestpage);

       } catch(Exception e) {

              mesg = "你要找的页码错误!";

       }

       book_list.setPage(pages);

}

%>

<table width="778">

  <tr>

     <td width="150" align="center">

     <%@include file="/bookshop/inc/left.inc"%>

     </td>

     <td width="600">

          <p align="center"><b><font color="#0000FF">隽隽电子书店图书<%= TransFormat.GB2unicode(classname) %>列表</font></b></p>

                    <%if (!keyword.equals("")) out.println("<p ><font color=#ff0000>你要查找关于&nbsp;" + keyword + "&nbsp;的图书如下</font></p>"); %>

          <table width="100%" border="1" cellspacing="1" cellpadding="1" bordercolor="white">

          <tr align="center" bgcolor="#DEF3CE">

            <td>图书名称</td>

            <td>作者</td>

            <td>图书类别</td>

            <td>出版社</td>

            <td>单价</td>

            <td width=110>选择</td>

          </tr>

<% if (book_list.book_search(request)) {

       if (book_list.getBooklist().size()>0 ){

              for (int i=0;i<book_list.getBooklist().size();i++){

                     book bk = (book) book_list.getBooklist().elementAt(i);%>

          <tr>

            <td><%=TransFormat.GB2unicode(bk.getBookName()) %></td>

            <td align="center"><%= (bk.getAuthor()) %></td>

            <td align="center"><%= (bk.getClassname()).getBytes("ISO-8859-1") %></td>

            <td align="center"><%= (bk.getPublish()) %></td>

            <td align="center"><%= bk.getPrince() %></td>

            <td align="center"><a href="#" οnclick="openScript('buy.jsp?bookid=<%= bk.getId() %>','pur',300,250)" >购买</a>&nbsp;

                     <a href="#" οnclick="openScript('detail.jsp?bookid=<%= bk.getId() %>','show',400,500)" >详细资料</a></td>

          </tr>

<%         }

       }else {

              if (keyword.equals("")){

                     out.println("<tr><td align='center' colspan=6>&nbsp;暂时没有此类图书资料</td></tr>");

              } else {

                     out.println("<tr><td align='center' colspan=6>&nbsp;没有你要查找的&nbsp;" + keyword + "&nbsp;相关图书</td></tr>")    ;

              }

       }

} else {%>

          <tr>           

            <td align="center" colspan=6>&nbsp;数据库出错,请稍后</td>

           

          </tr>

<% } %>

        </table>

        <table width="90%" border="0" cellspacing="1" cellpadding="1">

          <tr>

            <td align="right">总计结果为<%= book_list.getRecordCount() %>条,当前页第<%= book_list.getPage() %>页 <a href="booklist.jsp?classid=<%= classid%>&keyword=<%= keyword %>">首页</a>&nbsp;

              <% if (book_list.getPage()>1) {%>

              <a href="booklist.jsp?page=<%= book_list.getPage()-1 %>&classid=<%= classid%>&keyword=<%= keyword %>">上一页</a>&nbsp;

              <% } %>

              <% if (book_list.getPage()<book_list.getPageCount()-1) {%>

              <a href="booklist.jsp?page=<%= book_list.getPage()+1 %>&classid=<%= classid%>&keyword=<%= keyword %>">下一页</a>&nbsp;

              <% } %>

              <a href="booklist.jsp?page=<%= book_list.getPageCount() %>&classid=<%= classid%>&keyword=<%= keyword %>">未页</a>&nbsp;</td>

          </tr>

        </table>

    这样有什么不好,当然代码写在页面,那就是一个最大的不好之处。没有采用MVC模式。还有如果分页的部分复杂一点如果像上面图1一样,那么一个页面不是有很多的代码。

下面是另外一个例子:

     <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0 bgcolor="#FFFFFF">

                    <TBODY>

                    <TR>

                      <TD>页次:<B><%=intPage%></B>/<B><%=intPageCount%></B> 每页<B><%=intPageSize%></B> 本类软件<B><%=intRowCount%></B></TD>

                      <TD width="50%" align="center">

                                 <%//if (intPage!=1){%>

                            <A

                        href="sort.jsp?sortid=<%=classid%>&page=1"><FONT  face=Webdings>9</FONT></A><A    href="sort.jsp?sortid=<%=classid%>&page=<%=(intPage-1)%>"><FONT  face=Webdings>7</FONT></A>&nbsp;

                                      <%//}

                                      j=intPageCount;

                                      if(j>5) j=5;

                                      for(i=1;i<=j;i++){

                                      %>

                                      <A

                        href="sort.jsp?sortid=<%=classid%>&page=<%=(i)%>">[<%=i%>]</A>

                                       <%

                                       }

                                       conn.close();

                                       //if (intPage!=intPageCount){%>&nbsp;

                                      <A                  title=下一页

                    href="sort.jsp?sortid=<%=classid%>&page=<%=(intPage+1)%>"><FONT

                        face=Webdings>8</FONT></A>

<A title=最后页                         href="sort.jsp?sortid=<%=classid%>&page=<%=intPageCount%>"><FONT

                        face=Webdings>:</FONT></A>

                                      <%//}%></TD>

                      <TD>

                        <TABLE cellSpacing=0 cellPadding=0>

                          <FORM  method="post">

                          <TBODY>

                          <TR>          <TD>转到第<SELECT                               οnchange="javascript:goUrl(this.options[this.selectedIndex].value);"  name=select>

                                               <% for (i=1;i<=j;i++){%>

                                               <OPTION value=<%=i%> <%if(intPage==i)out.print("selected");%>><%=i%></OPTION>

                                                    <%}%>

                                                    </SELECT>

                                </TD></TR></TBODY>

                                </FORM>                     

                                </TABLE>

                                </TD></TR>

                                </TBODY></TABLE>

                                </TD></TR>

                                </TBODY>

                                </TABLE>

                                </TD></TR>

                                </TBODY></TABLE></TD>

    <TD vAlign=top width=4><IMG height=1

      src="images/c.gif" width=4

  border=0></TD></TR></TBODY>

  </TABLE>

  <script language="javascript">

  function goUrl(page)

  {  window.location.href="sort.jsp?sortid=<%=classid%>&page="+page;  } 

  </script>

这个代码是下载站的一个典型的应用。如上面图2。笔者在一个报销系统中用structs也实现了类似的分页。结果想用到别的系统中,又要修改。原因是这个和具体的数据耦合。而且它们也本质的相同之处:每次分页时都是同一个地址(如a.jsp,a.do等)每次页面的显示都要处理总的记录数,每页的记录数,还有下一页、上一页,当前页等。不同是有的总的记录数叫做“软件数”、“文章总数”之类的。如果能把样式和分页的结构分离出来多好?那样每个网站都能用。

        做过系统的人都会这样想,当然也会有人这样做。先不说那些国外的标签,就是我在网上就找了两个这样的标签。一个是jpager、一个是pagecontrol。当然这两个标签并不是很用。如果有时间还不如直接耦合数据。

先说说jpager(http://www.rosipay.com/1/22192.html能下载),它是根据用户到所有的数据传到页面,然后在页面上进行分页,具把相关的数据保存在会话中。这样就是至少有两个问题,一是如果数据非常大,有几百万条,能保存在会话中吗?还有如果一个系统有两种分页的的页面,这中间都用相同的属性名。那么就产生了会话中的分页错误。笔者在一个系统就是碰到这种会话错误。原来是不同页面的属性名相同。还有就是样式不能扩展。就只能是那么一种的样式。如果读者感兴趣,可以去看看源码。

对于pagecontrol,代码很简单,它采用MVC模式,每次传入页面需要的数据,它显示的样式有一定的可扩展性。因为它采用了MessageFormat.format()通过jsp文件来设定样式达到一定的扩展性。但是它对于如果不想采用MVC模式来说也是有一定的困难。

     那么有什么好的分页标签?能不能把两者综合起来,笔者自己实现了一个这样的标签。望读者指出错误。

      三分页标签

       就分页来讲,后台传入的数据无非两种形式,一种就是把所有的数据都传进来,一种是传入本页所需要的数据。对于采用那种方式,那得由用户根据自己的具体业务情况来决定。如果读者对模式很熟悉的话。肯定会脱口而出:典型的策略模式。采用策略模式的好处就不用讲,环境和策略分离。用户需要的只是知道在什么时候用什么策略。

模式的牛人都说模式从接口开始,当然我只能听从牛人的话,首先定义了一个paginateDao接口。环境类只要和用paginateDao接口就可以,那么对于分页来讲,都需要什么方法?看看下面的paginateUML图:     

在分页中,不论是采用哪种传入数据的方式,都应该给页面提供下面几个值(有的值是页面传到Dao,如pagesize,但还是传入之后进行分页的操作。然后传出来):pageSizedataSize也就是总的记录数,curragePagetotalPagesdataListpiginateDao就是围绕这个来操作。其下几个方法就是Boolean的判断当前页的下一页、上一页有效没有,还有是不是首页、末页等。这个paginateDao是借签ibatis中的分页技术。当然ibatis的分页技术是很差(笔者做了一个系统就是采用了它的分页,它采用分页技术是把所有的数据传进来)。

  给了接口,那么就要实现的方法,两种传入数据的方式都有一些共同的特征。可以把它们提到一个父类(抽象类)中。给了它们的Default的实现。从模式的角度来看。abstractPaginate应该是dao和具体实现的中间的Defalult Adapter。这个缺省还定义了抽象方法。从chart 3下面来看,abstractPaginate是一个很标准的模版方法模式。还是先看看:

 

接下来就是给具体的实现,先说一下FrontPaginateList类吧。它是传入全部的数据,然后由getDataint page)来实现具体的分页。对于这种方法,用户可以根据具体情况将它的实例放在会话中,还是request中。在会话中的话,就会出现上面jpager出现的问题一样。如果放在requestpage中,那么每次都要重新生成FrontPaginateList实例。这样好象性能也没有达到最优,如果读者有什么好的方法?Email: jljlpch@sohu.com., FrontPaginateList的实现很简单,当用户调用getData(int page)时,就会重新分页。重新分页就是pageSizecurragePagetotalPagesdataList这几个属性的改变。在这里有些读者可能不明白(源码)比如你调用setPageSize(int)并没有重新分页。其实不要像ibatis中实现的那样,因为你每次set 一些属性值,你最后都要落到getData(int page)方法上去。这是真正的调用分页。也只要在这时实现分页。可能不好理解。

对于AbstractAfterPaginate类,从名字就能看出是一个抽象类。为什么?因为我们的后台调取数据是随着参数变化而变化的。如(getUsers(int firstResult,int maxResult))而这个firstResult,maxResult是要从页面传来。也就是要通过PaginateDao来传到后台(数据库)去取数据。换句话说,当paginateDaocurrentPage状态发生变化,那么后台的firstResult,maxResult的状态也要发生变化。这就是一个Observer pattern 的应用。在dom4j就有一个相同的应用。还有在SpringHibernateDao中的回调函数也是同样的道理。当然就不用说SAX2中的应用啦。在这个例子中,我在想其实用Publish/Subscribe pattern这个词来形容更合适。说白了,观察者模式就是先定义一个抽象方法让用户去实现,这个方法是有状态的,所以是不能用参数的形式来传递的。用户可以采用匿名内部类或外部类的写法去生成一个对象。然后传给源对象。用匿名内部类包装 如下:
PageControlDao dao = new AbstractAfterPaginate (){
 public List getPartialData(int start, int max) throws Exception {
  return customerManager.getList(begin, max);
 }
 public int  getRecordCount()  throws Exception {
  return customerManager.getListCount();
 } }; H

 

外部类就是继承AbstractAfterPaginate 类如:

  Public  class UserListsPaginate extends  AbstractAfterPaginate{

public List getPartialData(int start, int max) throws Exception {
  return customerManager.getList(begin, max);
 }
 public int  getRecordCount()  throws Exception {
  return customerManager.getListCount();
 }}

这个UML现在应该是很简单的.

传完了数据,现在回到页面:对于页面上的标签应该怎么设计呢?<C:foreach>类似的循环中都要传入类似的List方式的数据(Set,Map)等。我们要控制分页就是主要控制传入到<C:foreach>类似的循环中的数据。在jpager中,在<C:foreach>类似的循环中还增加一个itemtag。这样去控制循环的个数有点大烦。其实不如像JSTL中的标签一样,把数据传到<page: control var=”传出的数据List”  datas=”传入的数据”> <C:foreach>类似的循环中用:<c: var=”item” items=”上面传出的数据”> 说实在,在这里用<displaytag>来处理循环是一个比较好的选择。而且<displaytag>分页的功能也不是很强,它实现分页也是传入全部的数据(?)。

在循环下面还有一个navigate。在这个地方,我们要根据具体要求去设定如:上一页、下一页、共多少页、多少记录之类的。表现不同。但内在的本质是一样。在页面上如果分页的话,也只要这个标签相呼应就可以。

知道要什么,那么应该怎么设计,能满足各种业务的需求呢。在displaytag中,用了大量的装饰模式。这里也可以用装饰模式。但这里只抽象一些公共的方法。

 

 

在UML中可以看出:只要传入实现AbstractPaginate的子类对象给item就可以。不管是哪一种数据传入方式,都可以把传入对象的当前页面的数据List给var变量。对于pagesize,没有指定,按default来定。PaginateControlTagSupport提供了所有的分页的一个一般的行为。对于每个需要特定的行为的分页可能继承这个类。比如,有的想采用读样式文件的方式去设定分页的页面,可以继承PaginateControlTagSupport类。这个API中也提供了一个default实现。

对于PaginateNavigateTagSupport类,和PaginateControlTagSupport一样。是所有的navigateTag的共同的抽象。不过actionpath传入时还是有点问题。这个和displayTag中的问题是一样。比如用brower的后退功能。不过在PaginateNavigateTagSupportdefault 实现中。对于传入的actionpath,还有一点问题,如果传了参数,(如a.do?id=”a”)那么就有错误。当然可以加上一个条件去判断。笔者没有做。

 

不过,笔者还没有去测试所有的代码。(所有的代码仅参考!)

 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
由《Ext JS源码分析与开发实例宝典》作者:彭仁夔团队开发而成 根据数据模型(如数据库、PDM等)可以生成直接运行的SSH项目,初始化数据库数据等。在当前的模板生成完善的CRUD操作,生成验证,根据不同的类型生成富文本,日历、树表结构等,并且还能生成完善强大的的实现权限系统。 功能之强大用了就知道! 两年前,我负责指导Java方向实训,在最后评审准备过程,.Net组负责人要求Java和.Net的归结一起统一评审,Java指导老师们的第一反应是Java和.Net开始怎么能相提并论? 为什么不能并论?是因为Java开发阳春白雪?在我们的潜意识,Java开发就是让程序员(学生)一行一行地编写代码,编写HTML、CSS等,而.Net开发只要把控件拖到页面上即可,Java开发的效率一定比.Net低很多很多。 那么能不能提高Java的开发效率呢?作者经过分析并总结思考,发现业务系统有一定的共性所有操作,即增删改查,既然有共性,那么能不能抽象出来? 可以思考一下,通过继承、组件化等重用设计方式是可以抽象代码的共性,但是不同表(实体)增删改查的内容是不同的,通过泛型也很难抽象出来的。如对于A表的查询和对于B表的查询都需要使用到SQL语句,怎么去剥离出来? 此时,我们就需要采用另外一种方式,把它们抽象出来更高层级的模板,然后把不同数据传入该模板的占位符,这样就变成不同的代码,这种方式就是代码生成技术。 在软件的设计过程,代码生成及重用设计仿若陌路人,互相排斥。重用设计很多的共性是不能抽象出来的,而一味追求生成,造成大量的雷同代码,是重用设计人员或真正开发人员最不能容忍的事件。 如果把它们两个结合起来,那就是完全不同的效果,首先通过重用设计抽象其能抽象的代码,其不能抽象的共性通过生成技术生成,开发效率就会成倍增加。以前我们都是在X或y轴上分别进行,其最大的点也不过是线,如果两者结合起来,其变成一个平面,把我们的重用从点提高到面。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值