随笔-34  评论-1965  文章-0  trackbacks-0

在《ssf入门》中,我曾经提过“虽然seam针对在jsf里进行数据分页和排序提供了解决方法,但我认为此方法过于简陋,而且不能使用richfaces的分页控件进行分页。通过ssf数据访问方式,您可以完美结合richfaces的分页控件简单地实现分页功能。”所以本文旨在给大家演示在ssf中,实现数据分页和排序是如何的方便和快捷。

效果预览

如果大家已经通过上一篇文章正确下载和配置了ssf的话,就已经可以通过菜单“管理->日志”查看分页和排序的效果,如下图所示。可能大家的环境里的日志可能还不足于超过一页的记录(10条),需要增加一些日志记录。要做到这点,只需重复几次登入、登出操作,当然为了使日志更加多样,可以在登入时尝试使用错误的密码或者不存在的用户。

分布效果图

实现原理

首先,我可以看一下日志页面对应的页面逻辑代码(ssf将这些代码称为action),如下清单所示。

 1 package com.whatisjee.ssf.web.action;
 2 
 3 import static com.whatisjee.ssf.web.jsf.facesutils.getservice;
 4 
 5 import java.io.serializable;
 6 
 7 import org.jboss.seam.scopetype;
 8 import org.jboss.seam.annotations.create;
 9 import org.jboss.seam.annotations.name;
10 import org.jboss.seam.annotations.scope;
11 
12 import com.whatisjee.ssf.domain.entity.log;
13 import com.whatisjee.ssf.domain.service.appservice;
14 import com.whatisjee.ssf.misc.logcriteria;
15 import com.whatisjee.ssf.misc.page;
16 import com.whatisjee.ssf.misc.paginglist;
17 import com.whatisjee.ssf.web.jsf.pagingdatamodel;
18 
19 @name("logsact")
20 @scope(scopetype.page)
21 public class logsaction implements serializable {
22     private static final long serialversionuid = -5252797451562495196l;
23     
24     private logcriteria criteria;
25     private pagingdatamodel<log> data;
26     private string detail;
27 
28     public logcriteria getcriteria() {
29         return criteria;
30     }
31 
32     public pagingdatamodel<log> getdata() {
33         return data;
34     }
35 
36     public string getdetail() {
37         return detail;
38     }
39 
40     public void setdetail(string detail) {
41         this.detail = detail;
42     }
43 
44     @create
45     public void init() {
46         criteria = new logcriteria();
47         data = new pagingdatamodel<log>("#{logsact.find}"false);
48     }
49     
50     public paginglist<log> find(page page) {
51         appservice service = getservice("appservice", appservice.class);
52         return service.findlogs(criteria, page);
53     }
54     
55     public void find() {
56         data.refresh();
57     }
58     
59     public void showdetail() {
60         log log = (log) data.getrowdata();
61         detail = log.getdetail();
62     }
63 }

如果朋友们有看过我之前的文章《seam的页面逻辑实现》,应该对上述代码不会陌生,值得注意的是类型分别为logcriteria和pagindatamodel的两个域,前者是为了封装界面上传过来的查询条件,后者则是整个数据分页和排序的关键。只需将它绑定到richfaces的数据列表组件(如等),即可实现分页和排序。上例中通过两个参数构造pagingdatamodel:第一个字符串参数el用来指明pagingdatamodel应该调用那个方法获得数据。它是一个el表达式,类似#{xxx.xxx}。前半部分的xxx多数情况下与action的名称相同,在本例中同为“loginact”。后半部分xxx就是数据获取方法的名称,这个方法必须有且只有一个参数而且类型为com.whatisjee.ssf.misc.page,返回值必须为com.whatisjee.ssf.misc.paginglist类型。page类包含分页和排序必不可少的信息如:从第几行开始获取数据,获取多少行,通过什么列进行排序,升序还是降序等。而paginglist则封装了总共有多少行和本页数据的信息。如本例的find方法通过将logcriteria和page传给appservice的findlogs方法,查询数据库获取数据。第二个布尔参数stateful指明pagingdatamodel是否需要自己保存状态。如果它是位于有状态的action中,即action的scope为application、session、conversation或者page,则无需自己保存状态,如本例中应为false。对于那些没有状态的action,即scope为request或event,应为true。另外,pagingdatamodel还有两个可选的构造参数:一个是page类型指明page首次加载数据时分页信息,另外一个字符串类型id仅用于在stateful为true且在一个页面上有多个pagingdatamodel时,将它们区分开来。

至于,详细的pagingdatamodel的实现大家如果有兴趣的话,可以过目一下,由于时间有限,我就不做详细解释了。

  1 package com.whatisjee.ssf.web.jsf;
  2 
  3 import static com.whatisjee.ssf.misc.page.order_asc;
  4 import static com.whatisjee.ssf.misc.page.order_desc;
  5 import static org.richfaces.model.ordering.ascending;
  6 import static org.richfaces.model.ordering.descending;
  7 
  8 import java.io.ioexception;
  9 import java.io.serializable;
 10 import java.util.collection;
 11 import java.util.collections;
 12 import java.util.list;
 13 import java.util.map;
 14 import java.util.set;
 15 
 16 import javax.el.elcontext;
 17 import javax.el.elexception;
 18 import javax.el.expressionfactory;
 19 import javax.el.methodexpression;
 20 import javax.faces.application.application;
 21 import javax.faces.component.uiviewroot;
 22 import javax.faces.context.facescontext;
 23 
 24 import org.ajax4jsf.model.datavisitor;
 25 import org.ajax4jsf.model.range;
 26 import org.ajax4jsf.model.sequencerange;
 27 import org.ajax4jsf.model.serializabledatamodel;
 28 import org.apache.commons.logging.log;
 29 import org.apache.commons.logging.logfactory;
 30 import org.richfaces.model.filterfield;
 31 import org.richfaces.model.modifiable;
 32 import org.richfaces.model.ordering;
 33 import org.richfaces.model.sortfield2;
 34 
 35 import com.whatisjee.ssf.misc.page;
 36 import com.whatisjee.ssf.misc.paginglist;
 37 import com.whatisjee.ssf.misc.page.order;
 38 
 39 /**
 40  * this pagingdatamodel is built to support "true" paging and sorting for richfaces's data iteration components
 41  *  e.g. <rich:datatable/>, <rich:datagrid/>. the "true" paging and sorting means those kinds of things can be
 42  *  done in database by sql.
 43  * @author max yuan(max.m.yuan@gmail.com)
 44  * @param  a class which must be serializable.
 45  */
 46 public class pagingdatamodel<extends serializable> extends serializabledatamodel implements
 47         modifiable {
 48     private static final long serialversionuid = -5956332259881655981l;
 49     private static final log _log = logfactory.getlog(pagingdatamodel.class);
 50     private static final string key_page = "page";
 51     private static final string key_list = "list";
 52     
 53     public static final int default_rows = 10;
 54     public static final string default_id = "default";
 55     
 56     private page page;
 57     private paginglist<t> list;
 58     private string el;
 59     private string keypage;
 60     private string keylist;
 61     private boolean stateful;
 62 
 63     private integer rowkey = new integer(0);
 64     private boolean cached, changed, modified;
 65     
 66     private static map<string, object> dummy_map = new map<string, object>() {
 67         public void clear() {}
 68 
 69         public boolean containskey(object key) { return false; }
 70 
 71         public boolean containsvalue(object value) { return false; }
 72 
 73         public set<java.util.map.entry<string, object>> entryset() { return null; }
 74 
 75         public object get(object key) { return null; }
 76 
 77         public boolean isempty() { return false; }
 78 
 79         public set<string> keyset() { return null; }
 80 
 81         public object put(string key, object value) { return null; }
 82 
 83         public void putall(map extends string, ? extends object> m) {}
 84 
 85         public object remove(object key) { return null; }
 86 
 87         public int size() { return 0; }
 88 
 89         public collection<object> values() { return null; }
 90     };
 91     
 92     
 93     /**
 94      * this is construction method to create a pagingdatamodel which you can fully control it's behavior.
 95      * @param el an el expression points to a method which must like "public paginglist xxx(page xxx)".
 96      * @param page to specify the total records and initial sorting criteria.
 97      * @param id use to identify this pagingdatamodel from another one.
 98      * @param stateful use to specify whether to keep the state to avoid loading data every time page is loaded.
 99      * if the managed bean who contains this pagingdatamodel can keep state, e.g. session scope bean, application scope bean,
100      * page scope(only available for seam) bean, then this parameter can be "false", otherwise please set it "true".
101      */
102     public pagingdatamodel(string el, page page, string id, boolean stateful) {
103         this.el = el;
104         this.keypage = key_page  "$"  id;
105         this.keylist = key_list  "$"  id;
106         this.stateful = stateful;
107 
108         map<string, object> attrs = getattributes();
109         object _page = attrs.get(keypage);
110         if(_page != null) {
111             this.page = (page) _page;
112             cached = true;
113         } else {
114             this.page = page;
115             attrs.put(keypage, page);
116         }
117     }
118     
119     /**
120      * this construction method create a pagingdatamodel with default amount of rows is 10.
121      * @param el an el expression points to a method which must like "public paginglist xxx(page xxx)".
122      * @param id use to identify this pagingdatamodel from another one.
123      * @param stateful use to specify whether to keep the state to avoid loading data every time page is loaded.
124      * if the managed bean which contains this pagingdatamodel can keep state, e.g. session scope bean, application scope bean,
125      * page scope(only available for seam) bean, then this parameter can be "false", otherwise please set it "true".
126      */
127     public pagingdatamodel(string el, string id, boolean stateful) {
128         this(el, new page(0, default_rows), id, stateful);
129     }
130     
131     /**
132      * this construction method create a pagingdatamodel with default amount of rows is 10 and the id set to be "default"
133      *  for only one pagingdatamodel in a page.
134      * @param el an el expression points to a method which must like "public paginglist xxx(page xxx)".
135      * @param stateful use to specify whether to keep the state to avoid loading data every time page is loaded.
136      * if the managed bean which contains this pagingdatamodel can keep state, e.g. session scope bean, application scope bean,
137      * page scope(only available for seam) bean, then this parameter can be "false", otherwise please set it "true".
138      */
139     public pagingdatamodel(string el, boolean stateful) {
140         this(el, new page(0, default_rows), default_id, stateful);
141     }
142     
143     /**
144      * this construction method create a pagingdatamodel with default amount of rows is 10 and can keep state.
145      * @param el an el expression points to a method which must like "public paginglist xxx(page xxx)".
146      * @param id use to identify this pagingdatamodel from another one.
147      */
148     public pagingdatamodel(string el, string id) {
149         this(el, new page(0, default_rows), id, true);
150     }
151     
152     /**
153      * this construction method create a pagingdatamodel with default amount of rows is 10, can keep state and
154      *  the id parameter set to be "default" for only one pagingdatamodel in a page.
155      * @param el an el expression points to a method which must like "public paginglist xxx(page xxx)".
156      */
157     public pagingdatamodel(string el) {
158         this(el, new page(0, default_rows), default_id, true);
159     }
160     
161     @override
162     public void update() {
163         // do nothing
164     }
165 
166     @override
167     public object getrowkey() {
168         return rowkey;
169     }
170 
171     @override 
172     public void setrowkey(object rowkey) {
173         this.rowkey = (integer) rowkey;
174     }
175     
176     private map<string, object> getattributes() {
177         map<string, object> attrs = null;
178         if(stateful){
179             uiviewroot root = facescontext.getcurrentinstance().getviewroot();
180             attrs = root.getattributes();
181         } else {
182             attrs = dummy_map;
183         }
184         return attrs;
185     }
186     
187     @suppresswarnings("unchecked")
188     private paginglist<t> getlist() {
189         if(changed) {
190             refresh();
191         } else if(stateful) {
192             list = (paginglist<t>) getattributes().get(keylist);
193         }
194         return list;
195     }
196 
197     @override
198     public void walk(facescontext context, datavisitor visitor, range range,
199             object argument) throws ioexception {
200         sequencerange seqrange = (sequencerange) range;
201         boolean isnew = page.getfirstrow() != seqrange.getfirstrow();
202         if (isnew) {
203             page.setfirstrow(seqrange.getfirstrow());
204         }
205         page.setrows(seqrange.getrows());
206         
207         if(!cached || isnew || modified) {
208             changed = true;
209             modified = false;
210             cached = true;
211         }
212 
213         int i = 0;
214         list<t> data = getlist().getdata();
215         if (data != null) {
216             for (@suppresswarnings("unused") t t : data) {
217                 visitor.process(context, i, argument);
218             }
219         }
220     }
221 
222     @override
223     public int getrowcount() {
224         paginglist<t> list = getlist();
225         return (list == null? 0 : list.getcount();
226     }
227 
228     @override
229     public object getrowdata() {
230         paginglist<t> list = getlist();
231         return (list == null || list.getdata() == null || rowkey == null? null
232                 : list.getdata().get(rowkey.intvalue());
233     }
234 
235     @override
236     public int getrowindex() {
237         throw new unsupportedoperationexception();
238     }
239 
240     @override
241     public object getwrappeddata() {
242         throw new unsupportedoperationexception();
243     }
244 
245     @override
246     public boolean isrowavailable() {
247         paginglist<t> list = getlist();
248         return (list != null&& (list.getdata() != null&& (rowkey != null)
249                 && (rowkey.intvalue() < list.getdata().size());
250     }
251 
252     @override
253     public void setrowindex(int arg0) {
254         throw new unsupportedoperationexception();
255     }
256 
257     @override
258     public void setwrappeddata(object arg0) {
259         throw new unsupportedoperationexception();
260     }
261 
262     /**
263      * use to execute the el method to refresh data manually.
264      */
265     @suppresswarnings("unchecked")
266     public void refresh() {
267         list = (paginglist<t>) evaluateel(el, paginglist.class,
268                 new class[] { page.class }, page);
269         if(list == null) {
270             list = paginglist.empty_list;
271         }
272         getattributes().put(keylist, list);
273         changed = false;
274     }
275     
276     private static object evaluateel(string el, class returntype,
277             class[] paramtypes, object params) {
278         object result = null;
279         facescontext fc = facescontext.getcurrentinstance();
280         elcontext elc = fc.getelcontext();
281         application app = fc.getapplication();
282         expressionfactory ef = app.getexpressionfactory();
283         methodexpression me = ef.createmethodexpression(elc, el, returntype, paramtypes);
284         try {
285             result = me.invoke(elc, params);
286         } catch (elexception e) {
287             throwable cause = e.getcause();
288             if(cause instanceof runtimeexception) {
289                 throw ((runtimeexception) cause);
290             } else {
291                 if(_log.iserrorenabled()) {
292                     _log.error("exception occured during evaluation of el, because ", cause);
293                 }
294             }
295         }
296         return result;
297     }
298     
299     @override
300     public  serializabledatamodel getserializablemodel(range range) {
301         return this;
302     }
303     
304     public page getpage() {
305         return page;
306     }
307     
308     @suppresswarnings("unchecked")
309     public list<t> getdata() {
310         paginglist<t> list = getlist();
311         return list != null ? list.getdata() : collections.empty_list;
312     }
313 
314     public void modify(list<filterfield> filterfields,
315             list<sortfield2> sortfields) {
316         if(sortfields != null && sortfields.size() > 0) {
317             sortfield2 sf = sortfields.iterator().next();
318             
319             string prop = sf.getexpression().getexpressionstring();
320             string orderby = prop.substring(2, prop.length() - 1);
321             if(!orderby.equals(page.getorderby())) {
322                 page.setorderby(orderby);
323                 modified = true;
324             }
325             
326             ordering ordering = sf.getordering();
327             order order = page.getorder();
328             if(ascending.equals(ordering) && !order_asc.equals(order)) {
329                 page.setorder(order_asc);
330                 modified = true;
331             } else if(descending.equals(ordering) && !order_desc.equals(order)) {
332                 page.setorder(order_desc);
333                 modified = true;
334             }
335         }
336     }
337 
338 }

再来看看页面的xhtml代码片段,如下列表所示。

 1 <rich:datatable id="dtlogs" value="#{logsact.data}" var="_log" rows="#{logsact.data.page.rows}" styleclass="list">
 2     <rich:column sortby="#{loggedat}" styleclass="align-c" headerclass="align-c" selfsorted="true" sortorder="descending">
 3         <f:facet name="header">
 4             <h:outputtext value="#{messages['log.loggedat']}"/>
 5         f:facet>
 6         <h:outputtext value="#{_log.loggedat}">
 7             <s:convertdatetime pattern="#{messages['cmm.shortdatetime']}" />
 8         h:outputtext>
 9     rich:column>
10     <rich:column sortby="#{username}" styleclass="align-l" headerclass="align-l">
11         <f:facet name="header">
12             <h:outputtext value="#{messages['log.username']}"/>
13         f:facet>
14         <h:outputtext value="#{_log.username}" />
15     rich:column>
16     <rich:column sortby="#{terminal}" styleclass="align-c" headerclass="align-c">
17         <f:facet name="header">
18             <h:outputtext value="#{messages['log.terminal']}"/>
19         f:facet>
20         <h:outputtext value="#{_log.terminal}" />
21     rich:column>
22     <rich:column sortby="#{severity}" styleclass="align-c" headerclass="align-c">
23         <f:facet name="header">
24             <h:outputtext value="#{messages['log.severity']}"/>
25         f:facet>
26         <h:outputtext value="#{messages[$c['log_labels'][_log.severity - 1][1]]}" />
27     rich:column>
28     <rich:column sortby="#{summary}" styleclass="align-l" headerclass="align-l">
29         <f:facet name="header">
30             <h:outputtext value="#{messages['log.summary']}"/>
31         f:facet>
32         <h:outputtext value="#{sf:getlogsummary(_log)}" />
33     rich:column>
34     <rich:column styleclass="align-c" headerclass="align-c">
35         <f:facet name="header">
36             <h:outputtext value="#{messages['log.detail']}"/>
37         f:facet>
38         <a4j:commandlink action="#{logsact.showdetail}" rerender="itdetail" oncomplete="richfaces.showmodalpanel('mpdetail')" rendered="#{not empty _log.detail}" styleclass="icon">
39             <h:graphicimage value="/common/image/view.gif" alt="#{messages['cmm.remove']}" />
40         a4j:commandlink>
41     rich:column>
42 rich:datatable>
43 <rich:datascroller for="dtlogs" />

这里也有几点需要注意:的rows应该如本例所示,直接使用pagingdatamodel里的page的rows域,以免产生不一至。另外,的sortby属性应为“#{xxx}”的形式,通常为数据库表的列名或hiberante的实体的域名称。

小结

本文简单地描述了ssf针对seam和jsf里的数据分页和排序问题的解决方法。只要大家在项目用到racefaces,相信上述有代码都会有所帮助。

posted on 2011-01-04 01:10 max 阅读(4421) 评论(2)  编辑  收藏 所属分类: seam系列

评论:
# re: 简便快捷的ssf数据分页与排序 2011-01-04 08:58 |
不错,写的非常详细,学习了  回复  
  
# re: 简便快捷的ssf数据分页与排序 2011-01-04 10:13 |
这些的代码估计也不认得我啊、、  回复  
  

只有注册用户后才能发表评论。


网站导航:
              
 
网站地图