在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