Version 4

    原文:开发基于JBoss AS 7.2.0的Java EE程序 - 07.翻页

    英文:JBoss AS 7.2.0 - Java EE application development - 07.EJB and JSF Pagination framework

     

    概述

    任何网站在显示大量的记录的时候,总是采用分页显示,本站 http://javaarm.com 也不例外。我们在本章描述的是 http://javaarm.com 的万能分页框架,它代码量小,但是很灵活、方便,本站的所有翻页显示,都是用该分页框架做的。

     

    这个框架花费了我很多的心血,希望对大家有用。


    1. EJB 分页查询框架

    1.1 ICriteria.java

    这是通用查询条件顶层接口

     

    public interface ICriteria {
    }

    1.2 XTwoParametersCondition.java

    这是带2个参数的查询条件

    import java.io.Serializable;

     

    public class XTwoParametersCondition implements ICriteria,Serializable {
        private static final long serialVersionUID = 1L;
       
        public final String attributeName;
        public final XCompareOperator compareOperator;
        public final Object value;
       
        public XTwoParametersCondition(String attributeName,XCompareOperator compareOperator, Object value){
            this.attributeName = attributeName;
            this.compareOperator = compareOperator;
            this.value = value;
        }
    }

    1.3 XCompareOperator.java

    这是比较类型,用于定义各种比较算子 对应 SQL/JPQL 里面比较符号。

    import java.io.Serializable;

     

    public class XCompareOperator implements Serializable {
        private static final long serialVersionUID = 1L;
        //
        public static final XCompareOperator eq = new XCompareOperator("eq","=");
        public static final XCompareOperator ne = new XCompareOperator("ne","<>");//"!=" is OK too!
        public static final XCompareOperator lt = new XCompareOperator("eq","<");
        public static final XCompareOperator le = new XCompareOperator("eq","<=");
        public static final XCompareOperator gt = new XCompareOperator("eq",">");
        public static final XCompareOperator ge = new XCompareOperator("eq",">=");
        public static final XCompareOperator like = new XCompareOperator("like","like");
       
        public final String sqlSymbol;
        public final String jpqlSymbol;
        public XCompareOperator(String sqlSymbol, String jpqlSymbol){
            this.sqlSymbol = sqlSymbol;
            this.jpqlSymbol = jpqlSymbol;
        }
    }

     

    1.4 XOrder.java

    这是排序类型。

     

    import java.io.Serializable;

     

    public class XOrder implements Serializable{
        private static final long serialVersionUID = 1L;
       
        public final String attributeName;//参与排序的属性的名字
        public final boolean asc;//升序
        public XOrder(String attributeName,boolean asc){
            this.attributeName = attributeName;
            this.asc = asc;
        }
    }

    1.5 XPager.java

    这是分页信息存储器

    import java.io.Serializable;
    import java.util.List;

     

    public class XPager implements Serializable {
        private static final long serialVersionUID = 1L;
        public static final int DEFAULT_PAGESIZE = 50;//50
        public static final int MINIMUM_PAGESIZE = 10;
        public static final int MAXIMUM_PAGESIZE = 100;
        public static final int MAX_PAGESIZE = 300;
        public static final int PAGENUM_START = 1;
       
        public static final String CRITERIA_FLAG = "?";
       
        public int pageSize = DEFAULT_PAGESIZE;//翻页 size
        public int pageNum = PAGENUM_START;//从1开始; 要获取的某页的数据
       
        public String wherePattern;//格式:((?)AND(?)) OR ?
        public List<ICriteria> conditionList;//查询条件约束列表,依次替代wherePattern中的 '?'
        //
        public List<XOrder> orderby;
    }

     

     

    1.6 QueryResult.java

     

    这是分页结果信息存储器。

    import java.io.Serializable;

    import java.util.ArrayList;

    import java.util.List;

     

    public class QueryResult implements Serializable {   

        private static final long serialVersionUID = 1L;   

       

        public int pageSize = XPager.DEFAULT_PAGESIZE;//翻页 size

        public int pageNum = XPager.PAGENUM_START;//从1开始; 要获取的某页的数据

        //

        public int totalRecords = 0;//返回给用户

        public int totalPages = XPager.PAGENUM_START;//返回给用户

       

        @SuppressWarnings("rawtypes")

        public List records = new ArrayList();//默认空列表

    }

     

    1.7 PagerHelper.java

    这是最关键的分页辅助类!

     

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;

     

    import javax.persistence.EntityManager;
    import javax.persistence.Query;

     

    import org.jboss.logging.Logger;

     

    public class PagerHelper {
        public static final Logger log = Logger.getLogger(PagerHelper.class);
       
        @SuppressWarnings("rawtypes")
        public static final QueryResult getResult(Class clz, EntityManager em, XPager pager){
            QueryResult queryResult = new QueryResult();
            try{
                /*************************************[预处理]*************************************/
                if(pager.pageNum<1){
                    pager.pageNum = XPager.PAGENUM_START;
                }

     

                if(pager.pageSize<XPager.MINIMUM_PAGESIZE || pager.pageSize>XPager.MAXIMUM_PAGESIZE){
                    pager.pageSize = XPager.DEFAULT_PAGESIZE;
                }
               
                //
                queryResult.pageNum = pager.pageNum;
                queryResult.pageSize = pager.pageSize;
               
                /*************************************[求总记录数]*************************************/
                HashMap<String,Object> parameterNameValuePairs = new HashMap<String,Object>();
                final String whereJpql = getWhereJpql(pager,parameterNameValuePairs).trim();
                final String orderbyJpql = getOrderbyJpql(pager).trim();
               
                final String countJpql = "SELECT COUNT(*) FROM "+ clz.getName()+" obj "+(whereJpql.equals("")?" ":" WHERE "+whereJpql);
                final String queryJpql = "SELECT obj FROM "+ clz.getName()+" obj "
                        +(whereJpql.equals("")?" ":" WHERE "+whereJpql)
                        +(orderbyJpql.equals("")?" ":" ORDER BY "+orderbyJpql);
                /*************************************[求总记录数]*************************************/
               
                int totalRecords = 0;
                try{
                    //
                    Query whereQuery = em.createQuery(countJpql);
                    Iterator<String> it = parameterNameValuePairs.keySet().iterator();
                    while(it.hasNext()){
                        String parameterName = it.next();
                        Object parameterValue = parameterNameValuePairs.get(parameterName);
                        whereQuery.setParameter(parameterName, parameterValue);
                    }
                    totalRecords = Integer.parseInt(whereQuery.getSingleResult().toString());               
                }catch(Exception e){
                    log.error(e);
                }
                queryResult.totalRecords = totalRecords;

     

                /*************************************[分页]*************************************/
                int remain = totalRecords % pager.pageSize;
                int totalPages = totalRecords / pager.pageSize + (remain==0?0:1);
                if(totalPages==0){//总记录为0 时,到这里可能依旧为0.
                    totalPages=1;
                }
                queryResult.totalPages = totalPages;
                //
                if(pager.pageNum>totalPages){
                    pager.pageNum = totalPages;
                    queryResult.pageNum = totalPages;
                }
                /*************************************[抓记录]*************************************/
                if(totalRecords==0){
                    queryResult.records = new ArrayList();
                }else{
                    Query query = em.createQuery(queryJpql)
                            .setFirstResult((pager.pageNum-1)*queryResult.pageSize)
                            .setMaxResults(queryResult.pageSize);
                    //
                    Iterator<String> it = parameterNameValuePairs.keySet().iterator();
                    while(it.hasNext()){
                        String parameterName = it.next();
                        Object parameterValue = parameterNameValuePairs.get(parameterName);
                        query.setParameter(parameterName, parameterValue);
                    }
                   
                    queryResult.records = query.getResultList();
                }
            }catch(Exception e){
                queryResult.records = new ArrayList();
            }
           
            return queryResult;
        }

     

        public static final String getWhereJpql(XPager pager, HashMap<String,Object> parameterNameValuePairs){
            //1. 判定参数是否合法******************************************
            if(pager.conditionList==null){
                log.debug("pager.conditionList is null");
                return "";
            }
            if(pager.wherePattern==null){
                throw new RuntimeException("pager.wherePattern should NOT be null if pager.conditionList is NOT null.");//return "";
            }
            if(pager.wherePattern.indexOf("?")<0){
                //log.debug("pager.wherePattern 不含条件占位符'?'");
                throw new RuntimeException("pager.wherePattern contains no '?'.");//return "";
            }
           
            int criteriaFlagNum = 0;
            String tmp = pager.wherePattern;
            while(tmp.indexOf('?')>=0){//该while()仅仅用于计算criteriaFlagNum.
                criteriaFlagNum++;
                tmp = tmp.replaceFirst("\\?", "");
            }
            if(criteriaFlagNum!=pager.conditionList.size()){
                String msg = "criteriaFlagNum is "+criteriaFlagNum+" and pager.conditionList.size() is "+pager.conditionList.size()+". They are NOT equal!";
                log.warn(msg);
                System.out.println(msg);
                return "";
            }
           
            //2. 正式构造*************************************************
            String wherePattern = pager.wherePattern;
            Iterator<ICriteria> it = pager.conditionList.iterator();
            while(it.hasNext()){
                ICriteria criteria = it.next();
                if(criteria instanceof XTwoParametersCondition){
                    XTwoParametersCondition c= (XTwoParametersCondition)criteria;
                    String attributeNameFlag = "_"+c.attributeName.replaceAll("\\.","_");//Example: c.attributeName="user.id"
                    //
                    String s = " obj."+c.attributeName+" "+c.compareOperator.jpqlSymbol+" :"+attributeNameFlag;
                    wherePattern = wherePattern.replaceFirst("\\?", s);
                    parameterNameValuePairs.put(attributeNameFlag, c.value);
                }
            }
            return wherePattern;
        }

     

     

     

        public static final String getOrderbyJpql(XPager pager){

            if(pager.orderby==null||pager.orderby.size()==0){

                return "";

            }

            //

            StringBuilder sb = new StringBuilder();

            Iterator<XOrder> it = pager.orderby.iterator();

            while(it.hasNext()){

                XOrder order = it.next();

                sb.append(" obj.").append(order.attributeName).append(" ").append(order.asc?"ASC":"DESC").append(",");

            }

            String s = sb.toString();

            int lastCommaIndex = s.lastIndexOf(",");

            return s.substring(0,lastCommaIndex);

        }

       

     

        public static void main(String args[]){

            testWhere();

            System.out.println("\n\n");

            testOrderBy();

        }

        @SuppressWarnings("serial")

        public static void testWhere(){

            XPager pager = new XPager();

            pager.wherePattern = " (? AND ?) OR (? AND ?) ";

            List<ICriteria> lst = new ArrayList<ICriteria>(){

                {

                    add(new XTwoParametersCondition("attribute1",XCompareOperator.eq,"xiang"));

                    add(new XTwoParametersCondition("attribute2",XCompareOperator.eq,"yingbing"));

                    add(new XTwoParametersCondition("age",XCompareOperator.ge,30));

                    add(new XTwoParametersCondition("weight",XCompareOperator.lt,150));

                    //add(new XTwoParametersCondition("MORE",XCompareOperator.lt,150));//

                }

            };       

            pager.conditionList = lst;       

            System.out.println(getWhereJpql(pager,new HashMap<String,Object>()));

        }

     

        @SuppressWarnings("serial")

        public static void testOrderBy(){

            XPager pager = new XPager();

            pager.wherePattern = " (? AND ?) OR (? AND ?) ";

            List<XOrder> lst = new ArrayList<XOrder>(){

                {

                    add(new XOrder("name1",true));

                    add(new XOrder("name2",false));

                }

            };       

            pager.orderby = lst;       

            System.out.println(getOrderbyJpql(pager));

        }

    }


    1.8 EJB Session 使用该EJB查询框架示例

    我们以查询 文章主题(Topic) 为例。

     

    (a) 接口

    import java.util.List;

    import com.ybxiang.forum.ejb.entity.Post;
    import com.ybxiang.forum.ejb.entity.Topic;
    import com.ybxiang.forum.query.QueryResult;
    import com.ybxiang.forum.query.XPager;

    public interface ITopicSession {
        public QueryResult pageTopic(XPager pager);
    }

    (b) 实现

     

    package com.ybxiang.forum.ejb.session;

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;

    import javax.annotation.security.PermitAll;
    import javax.ejb.EJB;
    import javax.ejb.EJBContext;
    import javax.ejb.Local;
    import javax.ejb.Stateless;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;

    import com.ybxiang.forum.ejb.entity.Topic;
    import com.ybxiang.forum.query.PagerHelper;
    import com.ybxiang.forum.query.QueryResult;
    import com.ybxiang.forum.query.XPager;

    @Stateless
    @Local(ITopicSession.class)
    public class TopicSession implements ITopicSession{
        @PersistenceContext()
        private EntityManager em;

        @PermitAll()
        public QueryResult pageTopic(XPager pager){
            return PagerHelper.getResult(Topic.class,em, pager);
        }
    }

     

     

     

    EJB这一层,就这些代码,我的设计是不是很简单呢?

     





    2. JSF MBean查询框架

     

    现在,我们来描述,如何在JSF MBean中使用我们的EJB查询框架。


    2.1 JSF MBean翻页框架

    我们定义了一个JSF MBean翻页框架,供所有的翻页JSF MBean扩展使用。

     

     

    package com.ybxiang.forum.jsfmbean;

     

    import java.util.List;

     

    import com.ybxiang.forum.query.QueryResult;

    import com.ybxiang.forum.query.XPager;

     

    public abstract class AbstractPagerMBean {

        protected QueryResult queryResult = null;

       

        /**

         * 不对JSF网开放!

         */

        protected abstract void paging();

     

     

        /**

         * 查询页面读取page信息:

         * (a) 点击“翻页链接”翻页时,从【 网页地址参数】中提取page参数。不从html form中提取!

         * (b) 点击“查询按钮”时,从html form中提取page参数。

         */

        public final int getPage(){

            Integer page =ParameterHelper.getInteger(ParameterHelper.URL_PARAMETER_NAME_PAGE_NUM);

            if(page==null){

                return XPager.PAGENUM_START;

            }

            if(page<XPager.PAGENUM_START){

                return XPager.PAGENUM_START;

            }

            return page;

        }

        /**

         * 供查询页面set:<h:inputHidden id="page" value="#{topicGroupingPagerMBean.page}" />

         */

        public void setPage(int page){

            //什么都不做

        }

       

     

       

        @SuppressWarnings("rawtypes")

        public final List getPagedRecords(){

            //synchronized(queryResult) //防止并发执行query()多次!不行!

            {

                if(queryResult==null){paging();}

                return queryResult.records;

            }

        }

     

        public final int getTotalRecords(){

            //synchronized(queryResult) //防止并发执行query()多次!不行!

            {

                if(queryResult==null){paging();}

                return queryResult.totalRecords;           

            }

        }

     

        public final int getTotalPages(){

            //synchronized(queryResult) //防止并发执行query()多次!不行!

            {

                if(queryResult==null){paging();}

                return queryResult.totalPages;           

            }

        }

    }



    2.2 JSF MBean查询举例

    我们以查询 文章主题(Topic) 为例。


    package com.ybxiang.forum.jsfmbean.topic;

     

    import java.util.ArrayList;

    import java.util.List;

    import java.util.logging.Logger;

     

    import com.ybxiang.forum.ejb.entity.Menu;

    import com.ybxiang.forum.ejb.session.IMenuSession;

    import com.ybxiang.forum.ejb.session.ITopicSession;

    import com.ybxiang.forum.jsfmbean.AbstractPagerMBean;

    import com.ybxiang.forum.jsfmbean.JSFHelper;

    import com.ybxiang.forum.jsfmbean.KeywordsHelper;

    import com.ybxiang.forum.jsfmbean.ParameterHelper;

    import com.ybxiang.forum.query.ICriteria;

    import com.ybxiang.forum.query.QueryResult;

    import com.ybxiang.forum.query.XCompareOperator;

    import com.ybxiang.forum.query.XOrder;

    import com.ybxiang.forum.query.XPager;

    import com.ybxiang.forum.query.XTwoParametersCondition;

     

    import javax.ejb.EJB;

    import javax.faces.bean.ManagedBean;

    import javax.faces.bean.RequestScoped;

     

    import org.jfree.util.Log;

     

     

    @ManagedBean

    @RequestScoped

    public class TopicGroupingPagerMBean extends AbstractPagerMBean{

        static final Logger logger = Logger.getLogger(TopicGroupingPagerMBean.class.getName());

        @EJB

        private IMenuSession menuSession;

        @EJB

        private ITopicSession topicSession;

       

        //*****************************************[地址参数]*****************************************//

        /**

         * public: list.xhtml需要取用

         */

        public Long getMid(){

            return ParameterHelper.getLong(ParameterHelper.URL_PARAMETER_NAME_MENU_ID);

        }

        /**

         * 供search时,JSF page set!

         */

        public void setMid(Long mid){

            //什么都不做

        }

       

        /**

         * 查询页面读取keywords信息:

         * (a) 点击“翻页链接”翻页时,从【 网页地址参数】中提取keywords参数。不从html form中提取!

         * (b) 点击“查询按钮”时,从html form中提取keywords参数。

         */

        public final String getKeywords(){

            String keywords = ParameterHelper.getString(ParameterHelper.URL_PARAMETER_NAME_KEYWORDS);

            if(keywords==null || keywords.trim().length()==0){

                return "";

            }

            try{

                keywords = java.net.URLDecoder.decode(keywords, ParameterHelper.ENCODING);

            }catch(Exception e){

                Log.error(e);

            }

            return keywords;

        }   

        /**

         * 供查询页面set:<h:inputHidden id="keywords" value="#{topicGroupingPagerMBean.keywords}" />

         */

        public void setKeywords(String keywords){

            //什么都不做

        }


       

        //*****************************************[翻页实现]*****************************************//

        /**

         * 不对JSF网页开放!

         */

        @SuppressWarnings("serial")

        protected void paging(){

            //

            logger.fine("querying...");

            if(queryResult!=null){return;}

            queryResult = new QueryResult();//作用:下面各种检测不通过时返回空结果集!

            //

            //获取必要参数***************************************

            final Long menuId = getMid();

            //参数有效性判定*************************************

            if(menuId<1){

                return;

            }

            //菜单是否对外开放******************

            Menu menu = menuSession.getMenu(menuId);

            if(!menu.isOpen()){//本栏目未开放

                logger.warning("该菜单["+menuId+"]未开放,只允许管理员查看!");

                if( ! JSFHelper.isCurrentUserAdministrator()){

                    //logger.warning("该菜单["+menuId+"]未开放;现在查看该菜单的不是管理员,有人试图窃取本菜单下信息!");

                    return;

                }

            }

            if(menu.isJaasRoleControlled()){//需要该菜单对应的JAAS Role才能访问

                if(! JSFHelper.isMenuOpenedToCurrentUser(menu)){

                    return;

                }

            }

           

            //待查询的关键字

            //SearchCriteriaMBean searchMBean = (SearchCriteriaMBean) JSFHelper.readFromSession(SearchCriteriaMBean.NAME);

            //String keywords = searchMBean==null?null:searchMBean.getKeywords4Topic();

            String keywords = getKeywords();

            //

            //构造翻页器*****************************************

            XPager pager = new XPager();

            pager.pageNum = getPage();

            pager.orderby = new ArrayList<XOrder>(){

                {

                    add(new XOrder("sortWeight",false));

                }

            };

           

            if(keywords==null || keywords.trim().length()==0){
                pager.wherePattern = "(?)";
                List<ICriteria> lst = new ArrayList<ICriteria>(){
                    {
                        add(new XTwoParametersCondition("menuId",XCompareOperator.eq,menuId));
                    }
                };
                pager.conditionList = lst;
            }else{
                final String sqlQueryString = KeywordsHelper.buildSqlQueryString(keywords);
                pager.wherePattern = "(?) and (?)";
                List<ICriteria> lst = new ArrayList<ICriteria>(){
                    {
                        add(new XTwoParametersCondition("menuId",XCompareOperator.eq,menuId));
                        add(new XTwoParametersCondition("title",XCompareOperator.like,sqlQueryString));
                    }
                };
                pager.conditionList = lst;
            }
            //查询***********************************************
            queryResult = topicSession.pageTopic(pager);

        }

     

       

        //*****************************************[界面动作]*****************************************//

        //一律转化为URL参数传进来,否则提交参数然后翻页会导致多次查询DB。

     

    }


    3. JSF页面分页显示查询结果框架

    3.1 翻页组件 - pager.xhml

    该翻页组件花费了我很多心血啊!

    <?xml version='1.0' encoding='UTF-8' ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core">
           
        <!-- bellow h:panelGroup is a 'div' because its layout is 'block' -->
        <h:panelGroup layout="block" style="text-align: right; margin-bottom:10px;">
       
            <h:outputLink disabled="#{page eq 1}" styleClass="marginHor10px"
                          value="#{fromWebPath}?#{sharedParameterValuePairs}&page=#{1}"><img src="#{request.contextPath}/img/arrow_begin.gif" class="img-nav"/>
            </h:outputLink>
            <h:outputLink disabled="#{page eq 1}" styleClass="marginHor10px"
                          value="#{fromWebPath}?#{sharedParameterValuePairs}&page=#{page-1}"><img src="#{request.contextPath}/img/arrow_back.gif" class="img-nav"/>
            </h:outputLink>
            <h:inputText class="marginLeft10px" maxlength="5" size="2" value="#{page}" onkeydown="if(event.keyCode==13) {var fixedValue=this.value>#{totalPages}?#{totalPages}:this.value;window.location='#{fromWebPath}?#{sharedParameterValuePairs}&page='+fixedValue; doane(event);}"/>
            <h:outputLink disabled="#{page eq totalPages}" styleClass="marginHor10px"
                          value="#{fromWebPath}?#{sharedParameterValuePairs}&page=#{page+1}"><img src="#{request.contextPath}/img/arrow_forward.gif" class="img-nav"/>
            </h:outputLink>
            <h:outputLink disabled="#{page eq totalPages}" styleClass="marginHor10px"
                          value="#{fromWebPath}?#{sharedParameterValuePairs}&page=#{totalPages}"><img src="#{request.contextPath}/img/arrow_end.gif" class="img-nav"/>
            </h:outputLink>
            <h:outputFormat styleClass="marginHor10px" value="#{messages['ybxiang.javaarm.pager.total.records']}" >
                <f:param value="#{totalRecords}" />
            </h:outputFormat>
            <h:outputText value="  " />
            <h:outputFormat styleClass="marginHor10px" value="#{messages['ybxiang.javaarm.pager.total.pages']}" >
                <f:param value="#{totalPages}" />
            </h:outputFormat>
        </h:panelGroup>

     

    </ui:composition>

     

    3.2 显示查询记录组件 - displayer_topicList.xhtml

    <?xml version='1.0' encoding='UTF-8' ?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

     

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                    xmlns:ui="http://java.sun.com/jsf/facelets"
                    xmlns:f="http://java.sun.com/jsf/core"
                    xmlns:h="http://java.sun.com/jsf/html">

     

        <h:dataTable style="font-size: 14px;" width="100%"
                     value="#{topicList}" var="topicItem"
                     rowClasses="table_odd_row,table_even_row" >
            <h:column headerClass="topicList_Title" >
                <f:facet name="header">
                    <h:outputText value="#{messages['ybxiang.javaarm.topic.title']}"/>
                </f:facet>
                <h:outputLink target="_blank"
                              rendered="#{topicItem.separator}"
                              disabled="#{not facesContextMBean.isCurrentUserAdministrator()}"
                              value="#{request.contextPath}/faces/display.xhtml">
                    <h:outputText value="♠♠♠♠♠♠"
                                  title="#{topicItem.title}:#{topicItem.sortWeight}" />
                    <f:param name="tid" value="#{topicItem.id}" />
                </h:outputLink>
                <h:outputLink target="_blank"
                              styleClass="linelessLink"
                              rendered="#{!topicItem.separator}"
                              value="#{request.contextPath}/faces/display.xhtml">
                    <h:outputText value="#{topicItem.title}"
                                  title="#{topicItem.sortWeight}" />
                    <h:graphicImage rendered="#{topicItem.excellent}" value="#{request.contextPath}/img/star.png" width="15" height="15" style="border:0px;"  />
                    <f:param name="tid" value="#{topicItem.id}" />
                </h:outputLink>
            </h:column>
            <h:column headerClass="topicList_Author" >
                <f:facet name="header">
                    <h:outputText value="#{messages['ybxiang.javaarm.author']}"/>
                </f:facet>
                <h:outputText rendered="#{topicItem.separator}" value="♠" />
                <h:outputLink rendered="#{!topicItem.separator}" value="#{request.contextPath}/faces/user.xhtml?uid=#{topicItem.authorId}" target="_blank">
                    <h:outputText value="#{userCachingMBean.getUserByIdFromCache(topicItem.authorId).username}" />
                </h:outputLink>
            </h:column>
            <h:column headerClass="topicList_Statistics" >
                <f:facet name="header">
                    <h:outputText value="#{messages['ybxiang.javaarm.hitsOfReply']}"/>
                </f:facet>
                <h:outputText rendered="#{topicItem.separator}" value="♠" />
                <h:outputText rendered="#{!topicItem.separator}" value="#{topicItem.hitsOfReply}" />
            </h:column>
        </h:dataTable>

     

    </ui:composition>

     

     

    3.3 查询结果显示页面举例 - list.xhtml

    <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"

                    xmlns:ui="http://java.sun.com/jsf/facelets"

                    xmlns:f="http://java.sun.com/jsf/core"

                    xmlns:h="http://java.sun.com/jsf/html"

                    template="/template/template.xhtml">

     

        <ui:define name="title">

            <h:outputText value="#{menuCachingMBean.getMenu(topicGroupingPagerMBean.mid).getDomain().getName()} &gt; #{menuCachingMBean.getMenu(topicGroupingPagerMBean.mid).getName()}" />

        </ui:define>

     

        <ui:define name="headMetaData">

            <link rel="stylesheet" type="text/css" media="all" href="#{request.contextPath}/css/dataTable.css" />

            <link rel="stylesheet" type="text/css" media="all" href="#{request.contextPath}/css/dataTable_topic.css" />

        </ui:define>

     

        <ui:define name="body">

           

            <h:panelGroup layout="block" rendered="true">

     

                <h:panelGroup layout="block" rendered="true">

                    <h:form id="form">

                        <h:inputHidden id="mid" value="#{topicGroupingPagerMBean.mid}" />
                        <h:inputHidden id="page" value="#{topicGroupingPagerMBean.page}" />

                        <h:outputText value="    " />

                        <h:inputText id="keywords" value="#{topicGroupingPagerMBean.keywords}" />

                       

                        <input type="button" value="#{messages['ybxiang.javaarm.search']}" onclick="var keywords=document.getElementById('form:keywords').value;window.location.href='#{request.contextPath}/faces/list.xhtml?mid=#{topicGroupingPagerMBean.mid}&keywords='+keywords" />

       

                        <ui:include src="/template/pager.xhtml" >

                            <ui:param name="sharedParameterValuePairs" value="mid=#{topicGroupingPagerMBean.mid}&keywords=#{topicGroupingPagerMBean.keywords}" />

                            <ui:param name="fromWebPath" value="#{request.contextPath}/faces/list.xhtml" />

                            <ui:param name="page" value="#{topicGroupingPagerMBean.page}" />

                            <ui:param name="totalRecords" value="#{topicGroupingPagerMBean.totalRecords}" />

                            <ui:param name="totalPages" value="#{topicGroupingPagerMBean.totalPages}" />

                        </ui:include>

                        <ui:include src="/template/displayer_topicList.xhtml" >

                            <ui:param name="topicList" value="#{topicGroupingPagerMBean.getPagedRecords()}" />

                        </ui:include>

                        <ui:include src="/template/pager.xhtml" >

                            <ui:param name="sharedParameterValuePairs" value="mid=#{topicGroupingPagerMBean.mid}&keywords=#{topicGroupingPagerMBean.keywords}" />

                            <ui:param name="fromWebPath" value="#{request.contextPath}/faces/list.xhtml" />

                            <ui:param name="page" value="#{topicGroupingPagerMBean.page}" />

                            <ui:param name="totalRecords" value="#{topicGroupingPagerMBean.totalRecords}" />

                            <ui:param name="totalPages" value="#{topicGroupingPagerMBean.totalPages}" />

                        </ui:include>

                    </h:form>

                </h:panelGroup>

     

            </h:panelGroup>

           

        </ui:define>

    </ui:composition>