在JSF和Richfaces的官方示例里面沒發現正經的數據庫分頁示例,于是自己輪了一個,還算比較滿意,分享出來。

struts等框架,視圖(jsp、freemarker等)直接獲取action中準備好的數據結果集合,請求下一頁數據的時候,同樣后臺action處理請求,把action中的數據集合用新的這一頁數據替換掉,然后渲染頁面,從而實現分頁。每次請求action的處理過程可以拿到頁號等信息,所以在action調用service的時候就可以使用這些信息,調用相應的方法做分頁數據查詢。

JSF結合Richfaces做這個事情和Struts等框架有有很大的區別。

rich:dataTable這個標記可以配合一個rich:dataScroller使用,達到ajax翻頁的效果,但是這時候dataTable的value實際上要求是一個ExtendedDataModel對象。每次翻頁的時候,頁面組件調用這個ExtendedDataModel對象的walk方法獲取目標頁的數據——注意這個過程是不經過Action的。也就是說,頁面第一次加載的時候,dataTable的value屬性是#{action.data},其中data屬性是一個ExtendedDataModel對象,此后翻頁的過程中,不再經過action。

基于這樣的不同,需要采用一種新的機制來實現數據查詢過程。action返回給視圖的data對象,不應該是查詢出來的某一頁的一個數據集合,而是一種查詢數據的能力

最終Action中的代碼如下:
 1 public class CommonAction{
 2 
 3     protected PagedDataModel data;    //用于列表展現的數據
 4 
 5     @PostConstruct
 6     public void init(){
 7         Map<String,Object> parameters = ;//查詢條件
 8         this.data = this.findPage(parameters);
 9     }
10 
11     /**
12      * 使用Service分頁查詢數據
13      * @return 
14      *     注意,由于JSF的結構,這里返回的其實并不是真正的查詢結果,而是一種查詢數據的能力。
15      *     這種能力返回給JSF的頁面組件作為value,頁面渲染的時候使用這個能力來獲取數據
16      */
17     protected PagedDataModel findPage(final Map<String,Object> parameters) {
18         int count = this.getService().count(propertyFilterList).intValue();
19         return new PagedDataModel(count, new DataProvider(){
20         
21         @Override
22         public List<?> getList(int firstRow, int maxResults) {
23             return CommonAction.this.getService().find(parameters, firstRow, maxResults);
24         }
25         });
26     }
27 }

findPage返回的是一個PagedDataMode(繼承ExtendedDataModel),其中包含了真正的查詢數據的能力:DataProvider。

DataProvider很簡單:
1 public interface DataProvider {
2 
3     List<?> getList(int firstRow, int maxResults);
4     
5 }

PagedDataMode稍微復雜一點,其中做了一下數據緩存:
  1 /**
  2  * 
  3  * 分頁數據模型,用于rich:dataTable
  4  * 
  5  * @author allan
  6  *
  8  */
  9 public class PagedDataModel extends ExtendedDataModel {
 10     private Integer currentId;
 11     private Map<Integer, Object> dataMap = new LinkedHashMap<Integer, Object>();
 12     protected Integer count;
 13     private int lastFirstRow = 0;
 14     private int lastMaxResults = 0;
 15     private SortProperties sortFields;
 16     
 17     private DataProvider dataProvider;
 18     
 19     public PagedDataModel(int count,DataProvider dataProvider){
 20         super();
 21         super.count = count;
 22         this.dataProvider = dataProvider;
 23     }
 24 
 25     /**
 26      * 最終獲取總記錄數的方法
 27      * @return
 28      */
 29     public int getCount() {
 30         return super.count;
 31     }
 32     
 33     /**
 34      * 最終獲取數據的方法
 35      * @param firstRow
 36      * @param maxResults
 37      * @return
 38      */
 39     public List<?> getList(int firstRow, int maxResults, SortProperties sortFields) {
 40         return this.dataProvider.getList(firstRow, maxResults, sortFields);
 41     }
 42 
 43 
 44     @Override
 45     public Object getRowKey() {
 46         return this.currentId;
 47     }
 48 
 49     @Override
 50     public void setRowKey(Object key) {
 51         this.currentId = (Integer) key;
 52     }
 53 
 54     @Override
 55     public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException {
 56         int firstRow = ((SequenceRange) range).getFirstRow();
 57         int maxResults = ((SequenceRange) range).getRows();
 58         if(maxResults <= 0) maxResults = Integer.MAX_VALUE;
 59         //數據是不存在
 60         if(firstRow != this.lastFirstRow || maxResults != this.lastMaxResults) {
 61             //獲取所需數據
 62             List<?> listRow = this.getList(firstRow, maxResults, this.sortFields);
 63             //記錄數據
 64             dataMap.clear();
 65             for(int i = 0; i < listRow.size(); i++) {
 66                 Object row = listRow.get(i);
 67                 int index = firstRow + i;
 68                 dataMap.put(index, row);
 69             }
 70             
 71             this.lastFirstRow = firstRow;
 72             this.lastMaxResults = maxResults;
 73         }
 74         //設置數據
 75         for(Integer index : dataMap.keySet()) {
 76             visitor.process(context, index, argument);
 77         }
 78     }
 79 
 80     @Override
 81     public int getRowCount() {
 82         if(count == null)
 83             count = this.getCount();
 84         return count;
 85     }
 86     
 87     @Override
 88     public boolean isRowAvailable() {
 89         if(dataMap == null) {
 90             return false;
 91         } else {
 92             return dataMap.containsKey(currentId);
 93         }
 94     }
 95 
 96     @Override
 97     public Object getRowData() {
 98         return dataMap.get(currentId);
 99     }
100     
101     @Override
102     public Object getWrappedData() {
103         return this.getList(0, Integer.MAX_VALUE, this.sortFields);
104     }
105 
106     @Override
107     public void setWrappedData(Object arg0) {
108         throw new UnsupportedOperationException();
109     }
110     
111     @Override
112     public int getRowIndex() {
113         throw new UnsupportedOperationException();
114     }
115 
116     @Override
117     public void setRowIndex(int arg0) {
118         throw new UnsupportedOperationException();
119     }
120 }
121