改进Spring中的分页技术

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 plain copy
  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 plain copy
  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 plain copy
  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,也没关系,自己实现此接口就行了。(接口实现起来很简单,但技术细节却非简单,Hibernate用户在此居于明显的优势)(来源 www.iocblog.net)

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

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

[java]  view plain copy
  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 plain copy
  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 plain copy
  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已经为我们检索出来的记录子集。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值