隨筆-34  評論-1965  文章-0  trackbacks-0

          在《SSF入門》中,我曾經提過“雖然Seam針對在JSF里進行數據分頁和排序提供了解決方法,但我認為此方法過于簡陋,而且不能使用Richfaces的分頁控件<rich:datascroller>進行分頁。通過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<Log>的兩個域,前者是為了封裝界面上傳過來的查詢條件,后者則是整個數據分頁和排序的關鍵。只需將它綁定到Richfaces的數據列表組件(如<rich:dataTable>、<rich:dataGrid>等),即可實現分頁和排序。上例中通過兩個參數構造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. &lt;rich:dataTable/&gt;, &lt;rich:dataGrid/&gt;. 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 <T> 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<T> 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<T> 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<T> 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<T> 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<T> 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 <i>el</i> 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" />

          這里也有幾點需要注意:<rich:dataTable>的rows應該如本例所示,直接使用PagingDataModel里的page的rows域,以免產生不一至。另外,<rich:column>的sortBy屬性應為“#{xxx}”的形式,通常為數據庫表的列名或Hiberante的實體的域名稱。

          小結

          本文簡單地描述了SSF針對SEAM和JSF里的數據分頁和排序問題的解決方法。只要大家在項目用到Racefaces,相信上述有代碼都會有所幫助。

          posted on 2011-01-04 01:10 Max 閱讀(4512) 評論(2)  編輯  收藏 所屬分類: Seam系列

          評論:
          # re: 簡便快捷的SSF數據分頁與排序 2011-01-04 08:58 | air nailer
          不錯,寫的非常詳細,學習了  回復  更多評論
            
          # re: 簡便快捷的SSF數據分頁與排序 2011-01-04 10:13 | ugg boots stores
          這些的代碼估計也不認得我啊、、  回復  更多評論
            
          主站蜘蛛池模板: 应用必备| 赣榆县| 攀枝花市| 黄龙县| 建始县| 开原市| 保定市| 海南省| 津南区| 赣州市| 连平县| 专栏| 西城区| 绍兴市| 垣曲县| 房产| 长寿区| 米脂县| 株洲县| 旌德县| 张家口市| 石屏县| 乐清市| 彩票| 兴宁市| 凌海市| 香港| 莎车县| 雷山县| 福州市| 横山县| 交口县| 三亚市| 莎车县| 搜索| 手游| 三门县| 巴南区| 达州市| 城口县| 安顺市|