在JSF中实现分页

 第一种方式: 对于大多数Web应用,分页都是必不可少的功能,当然在JSF中也一样,我在这里用两种方法来展示一下,如何在JSF中实现分页。本文假定你已经对JSF有了一些简单的了解,懂得基本配置和使用,并建立起一个blank项目。

    MyfacesApache基金会中的一个一级项目,除了实现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 ,以及各种其他的方法,但是都不是很 优雅 。在上个月底的时候,在 MyfacesMail List中也针对这个问题展开了一系列的讨论,最后有人总结了讨论中提出的比较好的方法,提出了以下的分页方法,也是目前实现的最为优雅的方法,也就是不对dataTabledataScrollor做任何修改,仅仅通过扩展DataModel来实现分页。

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

    接下来我们要将需要的页的数据封装到一个DataPage中去,这个类表示了我们需要的一页的数据,里面包含有三个元素:datasetSizestartRow,和一个用于表示具体数据的ListdatasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取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;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值