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接口。
- import java.io.Serializable;
- import java.util.List;
- /**
- *
- * @author Sarkuya
- */
- public interface PagedListHolder extends Serializable {
- public static final int DEFAULT_PAGE_SIZE = 10;
- public static final int DEFAULT_MAX_LINKED_PAGES = 10;
- public void setRecordsSubst(List recordsSubset);
- public void setRealRecordCount(long realRecordCount);
- /**
- * 設置每頁應有多少條記錄。
- */
- public void setPageSize(int pageSize);
- /**
- * 返回每頁共有多少條記錄
- */
- public int getPageSize();
- /**
- * 根據pageSize,返回共有多少頁
- */
- public int getPageCount();
- /**
- * 返回當前頁碼。
- * 首頁爲0
- */
- public int getPage();
- /**
- * 設置當前頁碼。
- * 首頁爲0
- */
- public void setPage(int page);
- /**
- * 設置圍繞當前頁最多可以顯示多少鏈接的頁數。
- * 此方法<strong>會</strong>影響getFirstLinkedPage()及getLastLinkedPage()
- */
- public void setMaxLinkedPages(int maxLinkedPages);
- /**
- * 返回圍繞當前頁最多可以顯示多少鏈接的頁數
- */
- public int getMaxLinkedPages();
- /**
- * 返回首頁的頁碼(來源 www.iocblog.net)
- */
- public int getFirstLinkedPage();
- /**
- * 返回最後一頁的頁碼
- */
- public int getLastLinkedPage();
- /**
- * 轉至前一頁。
- * 如果已經是首頁,則停在該頁。
- */
- public void previousPage();
- /**
- * 轉至下一頁。
- * 如果已經是最後一頁,則停在該頁。
- */
- public void nextPage();
- /**
- * 轉至首頁。
- */
- public void firstPage();
- /**
- * 轉至最後一頁
- */
- public void lastPage();
- /**
- * 返回總的記錄數
- */
- public long getNrOfElements();
- /**
- * 返回在當前頁面上的第一個記錄在所有記錄(從0開始)中的編號
- */
- public int getFirstElementOnPage();
- /**
- * 返回在當前頁面上的最後一個記錄在所有記錄(從0開始)中的編號
- */
- public int getLastElementOnPage();
- /**
- * 返回在當前頁面上的所有記錄
- */
- public List getPageList();
- }
setRecordsSubst()用於存放頁面顯示的記錄源,而setRealRecordCount()用於記錄滿足條件的記錄總數。
下面是此接口的實現:
- import java.util.List;
- import javax.servlet.http.HttpServletRequest;
- import org.springframework.web.bind.ServletRequestDataBinder;
- /**
- *
- * @author Sarkuya
- */
- public class RequestPagedListHolder implements PagedListHolder {
- private static int pageSize = DEFAULT_PAGE_SIZE;
- private static int maxLinkedPages = DEFAULT_MAX_LINKED_PAGES;
- private int page = 0;
- private List recordsSubset;
- private long realRecordCount;
- /** Creates a new instance of RequestPagedListHolder */
- public RequestPagedListHolder(HttpServletRequest request, long realRecordCount, PagedListProvider pagedListProvider) {
- setRealRecordCount(realRecordCount);
- ServletRequestDataBinder binder = new ServletRequestDataBinder(this);
- binder.bind(request);
- checkPageNavigation(request);
- setRecordsSubst(pagedListProvider.getRecordsSubset(getPageSize() * getPage(), getPageSize()));
- }
- private void checkPageNavigation(final HttpServletRequest request) {
- String pageNavAction = request.getParameter("pageNavAction");
- if (pageNavAction != null) {
- if (pageNavAction.equals("firstPage")) {
- firstPage();
- } else if (pageNavAction.equals("previousPage")) {
- previousPage();
- } else if (pageNavAction.equals("nextPage")) {
- nextPage();
- } else if (pageNavAction.equals("lastPage")) {
- lastPage();
- }
- }
- }
- public void setRecordsSubst(List recordsSubset) {
- this.recordsSubset = recordsSubset;
- }
- public void setRealRecordCount(long realRecordCount) {
- this.realRecordCount = realRecordCount;
- }
- public void setPageSize(int pageSize) {
- this.pageSize = pageSize;
- }
- public int getPageSize() {
- return pageSize;
- }
- public int getPageCount() {
- float nrOfPages = (float) getNrOfElements() / getPageSize();
- return (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages);
- }
- public int getPage() {
- if (page >= getPageCount()) {
- page = getPageCount() - 1;
- }
- return page;
- }
- public void setPage(int page) {
- this.page = page;
- }
- public void setMaxLinkedPages(int maxLinkedPages) {
- this.maxLinkedPages = maxLinkedPages;
- }
- public int getMaxLinkedPages() {
- return maxLinkedPages;
- }
- public int getFirstLinkedPage() {
- return Math.max(0, getPage() - (getMaxLinkedPages() / 2));
- }
- public int getLastLinkedPage() {
- return Math.min(getFirstLinkedPage() + getMaxLinkedPages() - 1, getPageCount() - 1);
- }
- public void previousPage() {
- if (!isAtFirstPage()) {
- page--;
- }
- }
- public void nextPage() {
- if (!isAtLastPage()) {
- page++;
- }
- }
- public void firstPage() {
- setPage(0);
- }
- public void lastPage() {
- setPage(getPageCount() - 1);
- }
- public long getNrOfElements() {
- return realRecordCount;
- }
- public int getFirstElementOnPage() {
- return (getPageSize() * getPage());
- }
- public int getLastElementOnPage() {
- int endIndex = getPageSize() * (getPage() + 1);
- return (endIndex > getNrOfElements() ? (int)getNrOfElements() : endIndex) - 1;
- }
- public List getPageList() {
- return recordsSubset;
- }
- public boolean isAtFirstPage() {
- return getPage() == 0;
- }
- public boolean isAtLastPage() {
- return getPage() == getPageCount() - 1;
- }
- }
此類有以下特點:
1. pageSize及maxLinkedPages均設爲static,這樣不因每個Request而改變。因此用戶不必每次顯示一個不同的頁面後都在UI中重新設置它們。
2. 在構造函數中包裝了所有的使用過程,既簡化了該類的使用,也保證了該類被正確初始化。
3. 摒棄了newPageSet變量,減少了各個方法的耦合強度。
4. 在Spring環境中使用了ServletRequestDataBinder,大大簡化了各個參數的讀取設置過程。
5. 通過回調機制,每次只檢索PagedListProvider所提供的記錄子集,節約了內存,提高了程序效率。
不難看出,PagedListProvider是個接口,只有一個方法:
- import java.util.List;
- /**
- *
- * @author Sarkuya
- */
- public interface PagedListProvider {
- public List getRecordsSubset(int firstResult, int maxResults);
- }
熟悉Hibernate的用戶知道,Hibernate中就是需要這兩個參數來實現分頁了。如果不使用Hibernate,也沒關係,自己實現此接口就行了。(接口實現起來很簡單,但技術細節卻非簡單,Hibernate用戶在此居於明顯的優勢)(來源 www.iocblog.net)
以上的兩個接口,一個實現類,便是經過改進後的分頁技術了。下面看其使用方法。
當用戶需要查看帶有分面功能的頁面時,都會由下面的方法處理:
- private void setPageListForView(HttpServletRequest request, ModelAndView mav, final String tableName) {
- long totalRecordsCount = adminService.getTotalRecordCount(tableName);
- PagedListProvider listProvider = new PagedListProvider() {
- public List getRecordsSubset(int firstResult, int maxResults) {
- return (List) adminService.listTable(tableName, firstResult, maxResults);
- }
- };
- PagedListHolder holder = new RequestPagedListHolder(request, totalRecordsCount, listProvider);
- mav.addObject("pagedRecords", holder);
- }<span style="background-color: rgb(249, 252, 254); font-family: Tahoma, sans-serif;"> </span>
這是一個簡單的方法,爲RequestPagedListHolder的構造函數準備好實參後,生成一個實例,將其置於頁面的一個名爲"pagedRecords"的attribute中,以供JSP讀取。
adminService.getTotalRecordCount()應不難實現。主要是getRecordsSubset()。Service層的listTable()如下:
- public Collection listTable(String tableName, int firstResult, int maxResults) throws DataAccessException {
- return ((HibernateDao) daoFactory.getDao(tableName)).findByCriteria(firstResult, maxResults);
- }
Dao層代碼:
- public Collection findByCriteria(int firstResult, int maxResults) throws DataAccessException {
- DetachedCriteria criteria = DetachedCriteria.forClass(getEntityClass());
- return getHibernateTemplate().findByCriteria(criteria, firstResult, maxResults);
- }
下面看看視圖層的使用。
......
<c:forEach items="${pagedRecords.pageList}" var="record">
......
</c:forEach>
......
通過JSTL方便地讀出pagedRecords變量的pageList屬性。重抄一下上面的RequestPagedListHolder代碼相應部分:
public List getPageList() {
return recordsSubset;
}
返回的正是Hibernate已經爲我們檢索出來的記錄子集。