Spring中有一个PagedListHolder,能够实现分页。

但此类有几个缺点:

1. 使用此类的代码比較繁琐
2. 此类存放的数据源是全部的记录集,即对于记录数为1000条的数据,即使我们仅仅需在一个页面中显示10条记录,每次均须要检索1000条记录出来,而且没有内在的缓存机制
3. 假设需将pageSize, maxLinkedPages这些一般为Session级的变量存于Session中。则必须在Session中存放PagedListHolder,从而导致大容量的数据经常撑满了Session
4. 仅仅是实现了Serializable标识接口。且getPage(), setPage(), setPageSize()方法中直接使用newPageSet (private) 的属性,不利于子类覆盖。

并且,内部类的各个方法耦合极强。

特定方法的使用必须信赖于某个方法或标志变量作为前提条件。

比較理想的情况是。依据每个HttpServletRequest产生一个PagesListHolder,无论记录总数有多少个。每次仅仅检索页面上所显示的记录。但将pageSize, maxLinkedPages设为Session级的效果。

鉴于上述几点,我从Spring原有的PagedListHolder抽取出一些必需的方法名作为接口,并以一个名为RequestPagedListHolder的类实现之。

以下是抽取出来的PagedListHolder接口。

[java] view plaincopy
  1. import java.io.Serializable;  
  2. import java.util.List;  
  3. /** 
  4.  * 
  5.  * @author Sarkuya 
  6.  */  
  7. public interface PagedListHolder extends Serializable {  
  8.     public static final int DEFAULT_PAGE_SIZE = 10;  
  9.     public static final int DEFAULT_MAX_LINKED_PAGES = 10;  
  10.       
  11.     public void setRecordsSubst(List recordsSubset);  
  12.     public void setRealRecordCount(long realRecordCount);  
  13.       
  14.     /** 
  15.      * 设置每页应有多少条记录。 
  16.      */  
  17.     public void setPageSize(int pageSize);  
  18.       
  19.     /** 
  20.      * 返回每页共同拥有多少条记录 
  21.      */  
  22.     public int getPageSize();  
  23.       
  24.     /** 
  25.      * 依据pageSize,返回共同拥有多少页 
  26.      */  
  27.     public int getPageCount();  
  28.       
  29.     /** 
  30.      * 返回当前页码。 
  31.      * 首页为0 
  32.      */  
  33.     public int getPage();  
  34.       
  35.     /** 
  36.      * 设置当前页码。

     

  37.      * 首页为0 
  38.      */  
  39.     public void setPage(int page);  
  40.       
  41.     /** 
  42.      * 设置环绕当前页最多能够显示多少链接的页数。 
  43.      * 此方法<strong>会</strong>影响getFirstLinkedPage()及getLastLinkedPage() 
  44.      */  
  45.     public void setMaxLinkedPages(int maxLinkedPages);  
  46.       
  47.     /** 
  48.      * 返回环绕当前页最多能够显示多少链接的页数 
  49.      */  
  50.     public int getMaxLinkedPages();  
  51.       
  52.     /** 
  53.      * 返回首页的页码(来源 www.iocblog.net) 
  54.      */  
  55.     public int getFirstLinkedPage();  
  56.       
  57.     /** 
  58.      * 返回最后一页的页码 
  59.      */  
  60.     public int getLastLinkedPage();  
  61.       
  62.       
  63.     /** 
  64.      * 转至前一页。

     

  65.      * 假设已经是首页。则停在该页。 
  66.      */  
  67.     public void previousPage();  
  68.       
  69.     /** 
  70.      * 转至下一页。 
  71.      * 假设已经是最后一页。则停在该页。

     

  72.      */  
  73.     public void nextPage();  
  74.       
  75.     /** 
  76.      * 转至首页。 
  77.      */  
  78.     public void firstPage();  
  79.       
  80.     /** 
  81.      * 转至最后一页 
  82.      */  
  83.     public void lastPage();  
  84.       
  85.     /** 
  86.      * 返回总的记录数 
  87.      */  
  88.     public long getNrOfElements();  
  89.       
  90.     /** 
  91.      * 返回在当前页面上的第一个记录在全部记录(从0開始)中的编号 
  92.      */  
  93.     public int getFirstElementOnPage();  
  94.       
  95.     /** 
  96.      * 返回在当前页面上的最后一个记录在全部记录(从0開始)中的编号 
  97.      */  
  98.     public int getLastElementOnPage();  
  99.       
  100.     /** 
  101.      * 返回在当前页面上的全部记录 
  102.      */  
  103.     public List getPageList();  
  104. }  

setRecordsSubst()用于存放页面显示的记录源,而setRealRecordCount()用于记录满足条件的记录总数。

以下是此接口的实现:

[java] view plaincopy
  1. import java.util.List;  
  2. import javax.servlet.http.HttpServletRequest;  
  3. import org.springframework.web.bind.ServletRequestDataBinder;  
  4. /** 
  5.  * 
  6.  * @author Sarkuya 
  7.  */  
  8. public class RequestPagedListHolder implements PagedListHolder {  
  9.     private static int pageSize = DEFAULT_PAGE_SIZE;  
  10.     private static int maxLinkedPages = DEFAULT_MAX_LINKED_PAGES;  
  11.       
  12.     private int page = 0;  
  13.     private List recordsSubset;  
  14.       
  15.     private long realRecordCount;  
  16.       
  17.     /** Creates a new instance of RequestPagedListHolder */  
  18.     public RequestPagedListHolder(HttpServletRequest request, long realRecordCount, PagedListProvider pagedListProvider) {  
  19.         setRealRecordCount(realRecordCount);  
  20.           
  21.         ServletRequestDataBinder binder = new ServletRequestDataBinder(this);  
  22.         binder.bind(request);  
  23.           
  24.         checkPageNavigation(request);  
  25.           
  26.         setRecordsSubst(pagedListProvider.getRecordsSubset(getPageSize() * getPage(), getPageSize()));  
  27.     }  
  28.     private void checkPageNavigation(final HttpServletRequest request) {  
  29.         String pageNavAction = request.getParameter("pageNavAction");  
  30.         if (pageNavAction != null) {  
  31.             if (pageNavAction.equals("firstPage")) {  
  32.                 firstPage();  
  33.             } else if (pageNavAction.equals("previousPage")) {  
  34.                 previousPage();  
  35.             } else if (pageNavAction.equals("nextPage")) {  
  36.                 nextPage();  
  37.             } else if (pageNavAction.equals("lastPage")) {  
  38.                 lastPage();  
  39.             }  
  40.         }  
  41.     }  
  42.       
  43.     public void setRecordsSubst(List recordsSubset) {  
  44.         this.recordsSubset = recordsSubset;  
  45.     }  
  46.       
  47.     public void setRealRecordCount(long realRecordCount) {  
  48.         this.realRecordCount = realRecordCount;  
  49.     }  
  50.       
  51.     public void setPageSize(int pageSize) {  
  52.         this.pageSize = pageSize;  
  53.     }  
  54.       
  55.     public int getPageSize() {  
  56.         return pageSize;  
  57.     }  
  58.       
  59.     public int getPageCount() {  
  60.         float nrOfPages = (float) getNrOfElements() / getPageSize();  
  61.         return (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ?

     nrOfPages + 1 : nrOfPages);  

  62.     }  
  63.       
  64.     public int getPage() {  
  65.         if (page >= getPageCount()) {  
  66.             page = getPageCount() - 1;  
  67.         }  
  68.         return page;  
  69.     }  
  70.       
  71.     public void setPage(int page) {  
  72.         this.page = page;  
  73.     }  
  74.       
  75.     public void setMaxLinkedPages(int maxLinkedPages) {  
  76.         this.maxLinkedPages = maxLinkedPages;  
  77.     }  
  78.       
  79.     public int getMaxLinkedPages() {  
  80.         return maxLinkedPages;  
  81.     }  
  82.       
  83.     public int getFirstLinkedPage() {  
  84.         return Math.max(0, getPage() - (getMaxLinkedPages() / 2));  
  85.     }  
  86.       
  87.     public int getLastLinkedPage() {  
  88.         return Math.min(getFirstLinkedPage() + getMaxLinkedPages() - 1, getPageCount() - 1);  
  89.     }  
  90.       
  91.     public void previousPage() {  
  92.         if (!isAtFirstPage()) {  
  93.             page--;  
  94.         }  
  95.     }  
  96.       
  97.     public void nextPage() {  
  98.         if (!isAtLastPage()) {  
  99.             page++;  
  100.         }  
  101.     }  
  102.       
  103.     public void firstPage() {  
  104.         setPage(0);  
  105.     }  
  106.       
  107.     public void lastPage() {  
  108.         setPage(getPageCount() - 1);  
  109.     }  
  110.       
  111.     public long getNrOfElements() {  
  112.         return realRecordCount;  
  113.     }  
  114.       
  115.     public int getFirstElementOnPage() {  
  116.         return (getPageSize() * getPage());  
  117.     }  
  118.       
  119.     public int getLastElementOnPage() {  
  120.         int endIndex = getPageSize() * (getPage() + 1);  
  121.         return (endIndex > getNrOfElements() ?

     (int)getNrOfElements() : endIndex) - 1;  

  122.     }  
  123.       
  124.     public List getPageList() {  
  125.         return recordsSubset;  
  126.     }  
  127.       
  128.     public boolean isAtFirstPage() {  
  129.         return getPage() == 0;  
  130.     }  
  131.       
  132.     public boolean isAtLastPage() {  
  133.         return getPage() == getPageCount() - 1;  
  134.     }  
  135. }  


此类有下面特点:

1. pageSize及maxLinkedPages均设为static,这样不因每一个Request而改变。因此用户不必每次显示一个不同的页面后都在UI中又一次设置它们。
2. 在构造函数中包装了全部的使用过程。既简化了该类的使用,也保证了该类被正确初始化。
3. 摒弃了newPageSet变量,降低了各个方法的耦合强度。
4. 在Spring环境中使用了ServletRequestDataBinder。大大简化了各个參数的读取设置过程。
5. 通过回调机制,每次仅仅检索PagedListProvider所提供的记录子集,节约了内存。提高了程序效率。

不难看出。PagedListProvider是个接口,仅仅有一个方法:

[java] view plaincopy
  1. import java.util.List;  
  2. /** 
  3.  * 
  4.  * @author Sarkuya 
  5.  */  
  6. public interface PagedListProvider {  
  7.     public List getRecordsSubset(int firstResult, int maxResults);  
  8. }  

熟悉Hibernate的用户知道。Hibernate中就是须要这两个參数来实现分页了。假设不使用Hibernate。也没关系。自己实现此接口即可了。(接口实现起来非常easy,但技术细节却非简单。Hibernate用户在此居于明显的优势)(来源 www.iocblog.net)

以上的两个接口。一个实现类。便是经过改进后的分页技术了。以下看其用法。

当用户须要查看带有分面功能的页面时。都会由以下的方法处理:

[java] view plaincopy
  1. private void setPageListForView(HttpServletRequest request, ModelAndView mav, final String tableName) {  
  2.         long totalRecordsCount = adminService.getTotalRecordCount(tableName);  
  3.         PagedListProvider listProvider = new PagedListProvider() {  
  4.             public List getRecordsSubset(int firstResult, int maxResults) {  
  5.                 return (List) adminService.listTable(tableName, firstResult, maxResults);  
  6.             }  
  7.         };  
  8.           
  9.         PagedListHolder holder = new RequestPagedListHolder(request, totalRecordsCount, listProvider);  
  10.           
  11.         mav.addObject("pagedRecords", holder);  
  12.     }<span style="background-color: rgb(249, 252, 254); font-family: Tahoma, sans-serif;">    </span>  

这是一个简单的方法,为RequestPagedListHolder的构造函数准备好实參后。生成一个实例。将其置于页面的一个名为"pagedRecords"的attribute中,以供JSP读取。

adminService.getTotalRecordCount()应不难实现。主要是getRecordsSubset()。

Service层的listTable()例如以下:

[java] view plaincopy
  1. public Collection listTable(String tableName, int firstResult, int maxResults) throws DataAccessException {  
  2.     return ((HibernateDao) daoFactory.getDao(tableName)).findByCriteria(firstResult, maxResults);  
  3. }  

Dao层代码:


[java] view plaincopy
  1. public Collection findByCriteria(int firstResult, int maxResults) throws DataAccessException {  
  2.        DetachedCriteria criteria = DetachedCriteria.forClass(getEntityClass());  
  3.        return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);  
  4.    }  

以下看看视图层的使用。

    ......
    <c:forEach items="${pagedRecords.pageList}" var="record">
      ......
    </c:forEach>
    ......

通过JSTL方便地读出pagedRecords变量的pageList属性。

重抄一下上面的RequestPagedListHolder代码对应部分:

    public List getPageList() {
        return recordsSubset;
    }

返回的正是Hibernate已经为我们检索出来的记录子集。