在JSF中实现分页

来源:百度文库 编辑:神马文学网 时间:2024/04/27 03:35:51
Source: http://space.itpub.net/?uid-8485249-action-viewspace-itemid-429910
前面一篇直接使用了Myfaces中的两个Component完成了一个简单的分页,这里将会介绍一种On-demand loading的方法来进行分页,仅仅在需要数据的时候加载。

    先来说一些题外话,为了实现这种方式的分页,公司里大约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.
 * 


 * 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.
 * 


 * 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;
    }