您的位置:首页 > Web前端 > JavaScript

在JSF中实现分页

2009-01-05 14:04 295 查看
 第一种方式:对于大多数Web应用,分页都是必不可少的功能,当然在JSF中也一样,我在这里用两种方法来展示一下,如何在JSF中实现分页。本文假定你已经对JSF有了一些简单的了解,懂得基本配置和使用,并建立起一个blank项目。    Myfaces是Apache基金会中的一个一级项目,除了实现JSF标准外,做了很多的扩展工作,在Myfaces包中有一个扩展包Tomahawk,我们将主要使用其中的两个Component实现分页:一个是<t:dataTable>,另一个是<t:dataScroller>,在第一篇里面,我们简易的组合这两个Component来实现一种简单,但并不高效的分页。
    下面的例子来自于Myfaces-Sample,我省去了其中和分页逻辑无关的内容,详细的例子可以下载Myfaces-Sample包或者访问http://www.irian.at/myfaces/home.jsf查看。
    第一部分:dataTable


<t:dataTable id="data"


                  var="car"


                  value="#{pagedSort.cars}"


                  rows="10">


    ……


</t:dataTable>



在这一部分中,dataTable绑定了一个backing bean - pagedSort中的cars属性,我们可以在这个属性中加入数据访问逻辑,从数据库或者其他来源取得用于显示的数据。比如我们可以通过Hibernate获取一个List,其中包含有我们用于显示的POJOs。注意,dataTable中的rows属性指的是每页的行数,是必须指定的,否则是无法进行分页的,如果在项目中会使用固定行数的分页,建议把这个值写在BaseBackingBean中,并暴露一个property,供页面调用,所以每次在页面中就可以这么写#{backingBean.pageSize}。

第二部分:dataScroller


    <t:dataScroller id="scroll_1"


                        for="data"


                        fastStep="10"


                        paginator="true"


                        paginatorMaxPages="9">


            <f:facet name="first" >


                <t:graphicImage url="images/arrow-first.gif" border="1" />


            </f:facet>


            <f:facet name="last">


                <t:graphicImage url="images/arrow-last.gif" border="1" />


            </f:facet>


            <f:facet name="previous">


                <t:graphicImage url="images/arrow-previous.gif" border="1" />


            </f:facet>


            <f:facet name="next">


                <t:graphicImage url="images/arrow-next.gif" border="1" />


            </f:facet>


            <f:facet name="fastforward">


                <t:graphicImage url="images/arrow-ff.gif" border="1" />


            </f:facet>


            <f:facet name="fastrewind">


                <t:graphicImage url="images/arrow-fr.gif" border="1" />


            </f:facet>


        </t:dataScroller>



   这里定义了我们用于分页的<t:dataScroller>,最主要的是配置该分页Component针对哪个dataTable进行分页的“for”属性,该属性与dataTable绑定,并对其进行分页,在这里,绑定了第一部分中的id="data"的dataTable,下面有很多的<t:facet>是指定分页的导航样式的,这里使用了图片作为导航,可以把他们改成文字形式的导航。
   当然这只是最简单,也是一种不推荐的分页方式,因为在每次进行分页的时候,将会从数据库中取回所有的记录放入List中,然后,dataScroller在对这个List进行分页,如果在数据量很大的情况下,这种方式显然是不符合要求的,假设每条记录占用1k内存,数据库中有100万条记录,每次要把这个List全部读取出来将占用1G内存。我们需要一种Load on demand方式的读取,也就是只在需要查看某页的时候读取该页的数据。

   另外一方面,JSF的生命周期中有多个阶段会调用到#{pagedSort.cars}中对应的方法,如果在这里调用了数据访问逻辑,就会在只显示一次页面的情况下进行多次数据库操作,也是相当的耗费资源的。

第二种方式:先来说一些题外话,为了实现这种方式的分页,公司里大约5-6个人做了半个多月的工作,扩展了dataTable,修改了dataScrollor,以及各种其他的方法,但是都不是很优雅。在上个月底的时候,在Myfaces的Mail List中也针对这个问题展开了一系列的讨论,最后有人总结了讨论中提出的比较好的方法,提出了以下的分页方法,也是目前实现的最为优雅的方法,也就是不对dataTable和dataScrollor做任何修改,仅仅通过扩展DataModel来实现分页。

    DataModel是一个抽象类,用于封装各种类型的数据源和数据对象的访问,JSF中dataTable中绑定的数据实际上被包装成了一个DataModel,以消除各种不同数据源和数据类型的复杂性,在前面一篇中我们访问数据库并拿到了一个List,交给dataTable,这时候,JSF会将这个List包装成ListDataModel,dataTable访问数据都是通过这个DataModel进行的,而不是直接使用List。

    接下来我们要将需要的页的数据封装到一个DataPage中去,这个类表示了我们需要的一页的数据,里面包含有三个元素:datasetSize,startRow,和一个用于表示具体数据的List。datasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取count即可,startRow表示该页的起始行在数据库中所有记录集中的位置。




/** *//**


 * A simple class that represents a "page" of data out of a longer set, ie a


 * list of objects together with info to indicate the starting row and the full


 * size of the dataset. EJBs can return instances of this type when returning


 * subsets of available data.


 */


public class DataPage






{


    private int datasetSize;


    private int startRow;


    private List data;






    /** *//**


     * Create an object representing a sublist of a dataset.


     * 


     * @param datasetSize


     *            is the total number of matching rows available.


     * 


     * @param startRow


     *            is the index within the complete dataset of the first element


     *            in the data list.


     * 


     * @param data


     *            is a list of consecutive objects from the dataset.


     */


    public DataPage(int datasetSize, int startRow, List data)




    

{


        this.datasetSize = datasetSize;


        this.startRow = startRow;


        this.data = data;


    }






    /** *//**


     * Return the number of items in the full dataset.


     */


    public int getDatasetSize()




    

{


        return datasetSize;


    }






    /** *//**


     * Return the offset within the full dataset of the first element in the


     * list held by this object.


     */


    public int getStartRow()




    

{


        return startRow;


    }






    /** *//**


     * Return the list of objects held by this object, which is a continuous


     * subset of the full dataset.


     */


    public List getData()




    

{


        return data;


    }


}

 

    接下来,我们要对DataModel进行封装,达到我们分页的要求。该DataModel仅仅持有了一页的数据DataPage,并在适当的时候加载数据,读取我们需要页的数据。

 




/** *//**


 * A special type of JSF DataModel to allow a datatable and datascroller to page


 * through a large set of data without having to hold the entire set of data in


 * memory at once.


 * <p>


 * Any time a managed bean wants to avoid holding an entire dataset, the managed


 * bean should declare an inner class which extends this class and implements


 * the fetchData method. This method is called as needed when the table requires


 * data that isn't available in the current data page held by this object.


 * <p>


 * This does require the managed bean (and in general the business method that


 * the managed bean uses) to provide the data wrapped in a DataPage object that


 * provides info on the full size of the dataset.


 */


public abstract class PagedListDataModel extends DataModel






{


    int pageSize;


    int rowIndex;


    DataPage page;






    /** *//**


     * Create a datamodel that pages through the data showing the specified


     * number of rows on each page.


     */


    public PagedListDataModel(int pageSize)




    

{


        super();


        this.pageSize = pageSize;


        this.rowIndex = -1;


        this.page = null;


    }






    /** *//**


     * Not used in this class; data is fetched via a callback to the fetchData


     * method rather than by explicitly assigning a list.


     */




    public void setWrappedData(Object o)




    

{


        if(o instanceof DataPage)




        

{


            this.page = (DataPage) o;


        }


        else




        

{


            throw new UnsupportedOperationException("setWrappedData");


        }


    }




    public int getRowIndex()




    

{


        return rowIndex;


    }






    /** *//**


     * Specify what the "current row" within the dataset is. Note that the


     * UIData component will repeatedly call this method followed by getRowData


     * to obtain the objects to render in the table.


     */




    public void setRowIndex(int index)




    

{


        rowIndex = index;


    }






    /** *//**


     * Return the total number of rows of data available (not just the number of


     * rows in the current page!).


     */




    public int getRowCount()




    

{


        return getPage().getDatasetSize();


    }






    /** *//**


     * Return a DataPage object; if one is not currently available then fetch


     * one. Note that this doesn't ensure that the datapage returned includes


     * the current rowIndex row; see getRowData.


     */


    private DataPage getPage()




    

{


        if (page != null)




        

{


            return page;


        }




        int rowIndex = getRowIndex();


        int startRow = rowIndex;


        if (rowIndex == -1)




        

{


            // even when no row is selected, we still need a page


            // object so that we know the amount of data available.


            startRow = 0;


        }




        // invoke method on enclosing class


        page = fetchPage(startRow, pageSize);


        return page;


    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息