前面一篇直接使用了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
 * 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
 * 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
 * size of the dataset. EJBs can return instances of this type when returning
 * subsets of available data.
 * subsets of available data.
 */
 
						*/
				
				
						
						 public
				 
				class
				 DataPage
				
				public
				 
				class
				 DataPage

 
				
						 {
				
				
						{
 private
						 
						int
						 datasetSize;
    
						private
						 
						int
						 datasetSize;
 private
						 
						int
						 startRow;
    
						private
						 
						int
						 startRow;
 private
						 List data;
    
						private
						 List data;


 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Create an object representing a sublist of a dataset.
     * Create an object representing a sublist of a dataset.
 *
     * 
 * 
								@param
								 datasetSize
     * 
								@param
								 datasetSize
 *            is the total number of matching rows available.
     *            is the total number of matching rows available.
 *
     * 
 * 
								@param
								 startRow
     * 
								@param
								 startRow
 *            is the index within the complete dataset of the first element
     *            is the index within the complete dataset of the first element
 *            in the data list.
     *            in the data list.
 *
     * 
 * 
								@param
								 data
     * 
								@param
								 data
 *            is a list of consecutive objects from the dataset.
     *            is a list of consecutive objects from the dataset.
 */
     
								*/
						
						
								
								 public
						 DataPage(
						int
						 datasetSize, 
						int
						 startRow, List data)
    
						public
						 DataPage(
						int
						 datasetSize, 
						int
						 startRow, List data)

 
    
						
								 {
						
						
								{
 this
								.datasetSize 
								=
								 datasetSize;
        
								this
								.datasetSize 
								=
								 datasetSize;
 this
								.startRow 
								=
								 startRow;
        
								this
								.startRow 
								=
								 startRow;
 this
								.data 
								=
								 data;
        
								this
								.data 
								=
								 data;
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Return the number of items in the full dataset.
     * Return the number of items in the full dataset.
 */
     
								*/
						
						
								
								 public
						 
						int
						 getDatasetSize()
    
						public
						 
						int
						 getDatasetSize()

 
    
						
								 {
						
						
								{
 return
								 datasetSize;
        
								return
								 datasetSize;
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Return the offset within the full dataset of the first element in the
     * Return the offset within the full dataset of the first element in the
 * list held by this object.
     * list held by this object.
 */
     
								*/
						
						
								
								 public
						 
						int
						 getStartRow()
    
						public
						 
						int
						 getStartRow()

 
    
						
								 {
						
						
								{
 return
								 startRow;
        
								return
								 startRow;
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Return the list of objects held by this object, which is a continuous
     * Return the list of objects held by this object, which is a continuous
 * subset of the full dataset.
     * subset of the full dataset.
 */
     
								*/
						
						
								
								 public
						 List getData()
    
						public
						 List getData()

 
    
						
								 {
						
						
								{
 return
								 data;
        
								return
								 data;
 }
    }
						
						
								
								 }
}
				
		 
		
		
				
						     
				
				接下来,我们要对DataModel进行封装,达到我们分页的要求。该DataModel仅仅持有了一页的数据DataPage,并在适当的时候加载数据,读取我们需要页的数据。
		
		
		
				 
				 /** */
				
						/**
				/** */
				
						/**
						
								
								 * A special type of JSF DataModel to allow a datatable and datascroller to page
 * 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
 * through a large set of data without having to hold the entire set of data in
 * memory at once.
 * memory at once.
 * <p>
 * <p>
 * Any time a managed bean wants to avoid holding an entire dataset, the managed
 * 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
 * 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
 * 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.
 * data that isn't available in the current data page held by this object.
 * <p>
 * <p>
 * This does require the managed bean (and in general the business method that
 * 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
 * the managed bean uses) to provide the data wrapped in a DataPage object that
 * provides info on the full size of the dataset.
 * provides info on the full size of the dataset.
 */
 
						*/
				
				
						
						 public
				 
				abstract
				 
				class
				 PagedListDataModel 
				extends
				 DataModel
				
				public
				 
				abstract
				 
				class
				 PagedListDataModel 
				extends
				 DataModel

 
				
						 {
				
				
						{
 int
						 pageSize;
    
						int
						 pageSize;
 int
						 rowIndex;
    
						int
						 rowIndex;
 DataPage page;
    DataPage page;


 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Create a datamodel that pages through the data showing the specified
     * Create a datamodel that pages through the data showing the specified
 * number of rows on each page.
     * number of rows on each page.
 */
     
								*/
						
						
								
								 public
						 PagedListDataModel(
						int
						 pageSize)
    
						public
						 PagedListDataModel(
						int
						 pageSize)

 
    
						
								 {
						
						
								{
 super
								();
        
								super
								();
 this
								.pageSize 
								=
								 pageSize;
        
								this
								.pageSize 
								=
								 pageSize;
 this
								.rowIndex 
								=
								 
								-
								1
								;
        
								this
								.rowIndex 
								=
								 
								-
								1
								;
 this
								.page 
								=
								 
								null
								;
        
								this
								.page 
								=
								 
								null
								;
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Not used in this class; data is fetched via a callback to the fetchData
     * Not used in this class; data is fetched via a callback to the fetchData
 * method rather than by explicitly assigning a list.
     * method rather than by explicitly assigning a list.
 */
     
								*/
						
						
								
								 
								
								 public
						 
						void
						 setWrappedData(Object o)
    
						public
						 
						void
						 setWrappedData(Object o)

 
    
						
								 {
						
						
								{
 if
								(o 
								instanceof
								 DataPage)
        
								if
								(o 
								instanceof
								 DataPage)

 
        
								
										 {
								
								
										{
 this
										.page 
										=
										 (DataPage) o;
            
										this
										.page 
										=
										 (DataPage) o;
 }
        }
								
								
										
										 else
        
								else
								
										
										 
										 
        
								
										 {
								
								
										{
 throw
										 
										new
										 UnsupportedOperationException(
										"
										setWrappedData
										"
										);
            
										throw
										 
										new
										 UnsupportedOperationException(
										"
										setWrappedData
										"
										);
 }
        }
								
								
										
										 }
    }
						
						
								
								 
								
								 public
						 
						int
						 getRowIndex()
    
						public
						 
						int
						 getRowIndex()

 
    
						
								 {
						
						
								{
 return
								 rowIndex;
        
								return
								 rowIndex;
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Specify what the "current row" within the dataset is. Note that the
     * Specify what the "current row" within the dataset is. Note that the
 * UIData component will repeatedly call this method followed by getRowData
     * UIData component will repeatedly call this method followed by getRowData
 * to obtain the objects to render in the table.
     * to obtain the objects to render in the table.
 */
     
								*/
						
						
								
								 
								
								 public
						 
						void
						 setRowIndex(
						int
						 index)
    
						public
						 
						void
						 setRowIndex(
						int
						 index)

 
    
						
								 {
						
						
								{
 rowIndex 
								=
								 index;
        rowIndex 
								=
								 index;
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Return the total number of rows of data available (not just the number of
     * Return the total number of rows of data available (not just the number of
 * rows in the current page!).
     * rows in the current page!).
 */
     
								*/
						
						
								
								 
								
								 public
						 
						int
						 getRowCount()
    
						public
						 
						int
						 getRowCount()

 
    
						
								 {
						
						
								{
 return
								 getPage().getDatasetSize();
        
								return
								 getPage().getDatasetSize();
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Return a DataPage object; if one is not currently available then fetch
     * Return a DataPage object; if one is not currently available then fetch
 * one. Note that this doesn't ensure that the datapage returned includes
     * one. Note that this doesn't ensure that the datapage returned includes
 * the current rowIndex row; see getRowData.
     * the current rowIndex row; see getRowData.
 */
     
								*/
						
						
								
								 private
						 DataPage getPage()
    
						private
						 DataPage getPage()

 
    
						
								 {
						
						
								{
 if
								 (page 
								!=
								 
								null
								)
        
								if
								 (page 
								!=
								 
								null
								)

 
        
								
										 {
								
								
										{
 return
										 page;
            
										return
										 page;
 }
        }
								
								
										
										 
										
										 int
								 rowIndex 
								=
								 getRowIndex();
        
								int
								 rowIndex 
								=
								 getRowIndex();
 int
								 startRow 
								=
								 rowIndex;
        
								int
								 startRow 
								=
								 rowIndex;
 if
								 (rowIndex 
								==
								 
								-
								1
								)
        
								if
								 (rowIndex 
								==
								 
								-
								1
								)

 
        
								
										 {
								
								
										{
 //
										 even when no row is selected, we still need a page
            
										//
										 even when no row is selected, we still need a page
 //
										 object so that we know the amount of data available.
            
										//
										 object so that we know the amount of data available.
										
												
												 startRow 
										=
										 
										0
										;
										
										            startRow 
										=
										 
										0
										;
 }
        }
								
								
										
										 
										
										 //
								 invoke method on enclosing class
        
								//
								 invoke method on enclosing class
								
										
										 page 
								=
								 fetchPage(startRow, pageSize);
								
								        page 
								=
								 fetchPage(startRow, pageSize);
 return
								 page;
        
								return
								 page;
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Return the object corresponding to the current rowIndex. If the DataPage
     * Return the object corresponding to the current rowIndex. If the DataPage
 * object currently cached doesn't include that index then fetchPage is
     * object currently cached doesn't include that index then fetchPage is
 * called to retrieve the appropriate page.
     * called to retrieve the appropriate page.
 */
     
								*/
						
						
								
								 
								
								 public
						 Object getRowData()
    
						public
						 Object getRowData()

 
    
						
								 {
						
						
								{
 if
								 (rowIndex 
								<
								 
								0
								)
        
								if
								 (rowIndex 
								<
								 
								0
								)

 
        
								
										 {
								
								
										{
 throw
										 
										new
										 IllegalArgumentException(
            
										throw
										 
										new
										 IllegalArgumentException(
 "
										Invalid rowIndex for PagedListDataModel; not within page
										"
										);
                    
										"
										Invalid rowIndex for PagedListDataModel; not within page
										"
										);
 }
        }
								
								
										
										 
										
										 //
								 ensure page exists; if rowIndex is beyond dataset size, then
        
								//
								 ensure page exists; if rowIndex is beyond dataset size, then
 //
								 we should still get back a DataPage object with the dataset size
        
								//
								 we should still get back a DataPage object with the dataset size
 //
								 in it
        
								//
								 in it 
								
										
										 if
								 (page 
								==
								 
								null
								)
								
								        
								if
								 (page 
								==
								 
								null
								)

 
        
								
										 {
								
								
										{
 page 
										=
										 fetchPage(rowIndex, pageSize);
            page 
										=
										 fetchPage(rowIndex, pageSize);
 }
        }
								
								
										
										 
										
										 int
								 datasetSize 
								=
								 page.getDatasetSize();
        
								int
								 datasetSize 
								=
								 page.getDatasetSize();
 int
								 startRow 
								=
								 page.getStartRow();
        
								int
								 startRow 
								=
								 page.getStartRow();
 int
								 nRows 
								=
								 page.getData().size();
        
								int
								 nRows 
								=
								 page.getData().size();
 int
								 endRow 
								=
								 startRow 
								+
								 nRows;
        
								int
								 endRow 
								=
								 startRow 
								+
								 nRows;

 if
								 (rowIndex 
								>=
								 datasetSize)
        
								if
								 (rowIndex 
								>=
								 datasetSize)

 
        
								
										 {
								
								
										{
 throw
										 
										new
										 IllegalArgumentException(
										"
										Invalid rowIndex
										"
										);
            
										throw
										 
										new
										 IllegalArgumentException(
										"
										Invalid rowIndex
										"
										);
 }
        }
								
								
										
										 
										
										 if
								 (rowIndex 
								<
								 startRow)
        
								if
								 (rowIndex 
								<
								 startRow)

 
        
								
										 {
								
								
										{
 page 
										=
										 fetchPage(rowIndex, pageSize);
            page 
										=
										 fetchPage(rowIndex, pageSize);
 startRow 
										=
										 page.getStartRow();
            startRow 
										=
										 page.getStartRow();
 }
        }
								
								
										
										 else
								 
								if
								 (rowIndex 
								>=
								 endRow)
        
								else
								 
								if
								 (rowIndex 
								>=
								 endRow)

 
        
								
										 {
								
								
										{
 page 
										=
										 fetchPage(rowIndex, pageSize);
            page 
										=
										 fetchPage(rowIndex, pageSize);
 startRow 
										=
										 page.getStartRow();
            startRow 
										=
										 page.getStartRow();
 }
        }
								
								
										
										 return
								 page.getData().get(rowIndex 
								-
								 startRow);
        
								return
								 page.getData().get(rowIndex 
								-
								 startRow);
 }
    }
						
						
								
								 
								
								 public
						 Object getWrappedData()
    
						public
						 Object getWrappedData()

 
    
						
								 {
						
						
								{
 return
								 page.getData();
        
								return
								 page.getData();
 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Return true if the rowIndex value is currently set to a value that
     * Return true if the rowIndex value is currently set to a value that
 * matches some element in the dataset. Note that it may match a row that is
     * matches some element in the dataset. Note that it may match a row that is
 * not in the currently cached DataPage; if so then when getRowData is
     * not in the currently cached DataPage; if so then when getRowData is
 * called the required DataPage will be fetched by calling fetchData.
     * called the required DataPage will be fetched by calling fetchData.
 */
     
								*/
						
						
								
								 
								
								 public
						 
						boolean
						 isRowAvailable()
    
						public
						 
						boolean
						 isRowAvailable()

 
    
						
								 {
						
						
								{
 DataPage page 
								=
								 getPage();
        DataPage page 
								=
								 getPage();
 if
								 (page 
								==
								 
								null
								)
        
								if
								 (page 
								==
								 
								null
								)

 
        
								
										 {
								
								
										{
 return
										 
										false
										;
            
										return
										 
										false
										;
 }
        }
								
								
										
										 
										
										 int
								 rowIndex 
								=
								 getRowIndex();
        
								int
								 rowIndex 
								=
								 getRowIndex();
 if
								 (rowIndex 
								<
								 
								0
								)
        
								if
								 (rowIndex 
								<
								 
								0
								)

 
        
								
										 {
								
								
										{
 return
										 
										false
										;
            
										return
										 
										false
										;
 }
        }
								
								
										
										 else
								 
								if
								 (rowIndex 
								>=
								 page.getDatasetSize())
        
								else
								 
								if
								 (rowIndex 
								>=
								 page.getDatasetSize())

 
        
								
										 {
								
								
										{
 return
										 
										false
										;
            
										return
										 
										false
										;
 }
        }
								
								
										
										 else
        
								else
								
										
										 
										 
        
								
										 {
								
								
										{
 return
										 
										true
										;
            
										return
										 
										true
										;
 }
        }
								
								
										
										 }
    }
						
						
								
								 
								
								 
								 /** */
						
								/**
    
						/** */
						
								/**
								
										
										 * Method which must be implemented in cooperation with the managed bean
     * Method which must be implemented in cooperation with the managed bean
 * class to fetch data on demand.
     * class to fetch data on demand.
 */
     
								*/
						
						
								
								 public
						 
						abstract
						 DataPage fetchPage(
						int
						 startRow, 
						int
						 pageSize);
    
						public
						 
						abstract
						 DataPage fetchPage(
						int
						 startRow, 
						int
						 pageSize);
 
    
 }
}
				
		 
		
		
				
						     
				
				最后,我们需要在Backing Bean中加一些东西,调用业务逻辑,并将数据交给PagedListDataModel,来帮我们完成最后的分页工作。
		
		
				 
				 public
				 SomeManagedBean
				  
				public
				 SomeManagedBean 
				
						 {
				
				
						{
 
     .
.



 private
						 DataPage getDataPage(
						int
						 startRow, 
						int
						 pageSize)
    
						private
						 DataPage getDataPage(
						int
						 startRow, 
						int
						 pageSize) 
						
								 {
						
						
								{
 //
								 access database here, or call EJB to do so
      
								//
								 access database here, or call EJB to do so
								
										
										 }
								
								    }
						
						
								
								 
								
								 
								 public
						 DataModel getDataModel()
    
						public
						 DataModel getDataModel() 
						
								 {
						
						
								{

 if
								 (dataModel 
								==
								 
								null
								)
        
								if
								 (dataModel 
								==
								 
								null
								) 
								
										 {
								
								
										{
 dataModel 
										=
										 
										new
										 LocalDataModel(20);
            dataModel 
										=
										 
										new
										 LocalDataModel(20);
 }
        }
								
								
										
										 
										
										 return
								 dataModel;
        
								return
								 dataModel;
 }
    }
						
						
								
								 
								
								 
								 private
						 
						class
						 LocalDataModel 
						extends
						 PagedListDataModel
    
						private
						 
						class
						 LocalDataModel 
						extends
						 PagedListDataModel 
						
								 {
						
						
								{

 public
								 LocalDataModel(
								int
								 pageSize)
        
								public
								 LocalDataModel(
								int
								 pageSize) 
								
										 {
								
								
										{
 super
										(pageSize);
            
										super
										(pageSize);
 }
        }
								
								
										
										 
        

 public
								 DataPage fetchPage(
								int
								 startRow, 
								int
								 pageSize)
        
								public
								 DataPage fetchPage(
								int
								 startRow, 
								int
								 pageSize) 
								
										 {
								
								
										{
 //
										 call enclosing managed bean method to fetch the data
            
										//
										 call enclosing managed bean method to fetch the data
										
												
												 return
										 getDataPage(startRow, pageSize);
										
										            
										return
										 getDataPage(startRow, pageSize);
 }
        }
								
								
										
										 }
}
						
				
		 
		
				
						
								
						
				
		
		
				这里面有一个getDataPage的方法,只需要把所有业务逻辑的调用放在这里就可以了,最后业务逻辑调用的结果返回一个List,总条数返回一个int型的count放到DataPage中去就可以了。
		
		
				为了实现复用,把上面第三段的代码中的LocalDataModel类和getDataPage方法抽到BasePagedBackingBean中,把getDataPage方法改成:
		
		
				protected abstract DataPage getDataPage(int startRow, int pageSize);
		
		
				这样我们把所有需要分页的Backing Bean继承自这个抽象类,并实现getDataPage方法即可很容易的实现分页。
		
		
				
						 
				
		
		
				   在具体应用中可以这么写:
		
		
				 protected
				 DataPage getDataPage(
				int
				 startRow, 
				int
				 pageSize)
				    
				protected
				 DataPage getDataPage(
				int
				 startRow, 
				int
				 pageSize)

 
    
				
						 {
				
				
						{
 List scheduleList 
						=
						 scheduleService.getSchedulesByDate(scheduleDate, startRow, pageSize);
        List scheduleList 
						=
						 scheduleService.getSchedulesByDate(scheduleDate, startRow, pageSize);
 int
						 dataSetSize 
						=
						 scheduleService.getSchedulesCountByDate(scheduleDate);
        
						int
						 dataSetSize 
						=
						 scheduleService.getSchedulesCountByDate(scheduleDate);
 return
						 
						new
						 DataPage(dataSetSize, startRow, scheduleList);
        
						return
						 
						new
						 DataPage(dataSetSize, startRow, scheduleList);
 }
    }
				
				
						
						 
				
		 
		
				
		
		
				在数据访问中,我们只需要取出我们需要行数的记录就可以了,这在hibernate中非常容易实现。
		
		
				如果使用Criteria查询的话,只要加上:
		
		
				
						     criteria.setFirstResult(startRow);
		
		
				
						     criteria.setMaxResults(pageSize);
		
		
				使用Query查询的话,只要加上
		
		
				
						     query.setFirstResult(startRow);
		
		
				
						     query.setMaxResults(pageSize);
		
		
				
						
						
				
				并把两个参数传入即可。
		
		
				
						
						
				
				我们还需要另外写一个Count的DAO,取出相同查询条件的记录条数即可。
		
		
				还要修改一下Backing Bean中与dataTable绑定的property,将返回类型由List改成DataModel,而第一篇中用到的页面不需要做任何修改就可以满足新的需求了。
		
		
				里面最重要的是 PagedListDataModel 中 fetchPage 这个方法,当满足取数据的条件时,都会调用它取数据,因为业务逻辑不同,不便于将业务逻辑的调用放在里面实现,于是将其作为抽象方法,将具体的实现放到具体的Backing Bean中进行,在BaseBackingBean中,实现了这个方法,调用了getDataPage(startRow, pageSize)这个方法,而在BaseBackingBean中,这个方法又推迟到更具体的页面中实现,这样,我们在具体的页面中只需要实现一个getDataPage(startRow, pageSize)这个方法访问业务逻辑。
		
		
				
						
						
				
				大功告成,这个实现把前面遇到的两个问题都解决了,
				On-demand loading
				是没有问题了,因为只有在首次读取和换页的时候DataModel才会向数据库请求数据,虽然在JSF的生命周期中多次调用与dataTable绑定的方法,但是因为每次业务逻辑请求以后,数据都会存放在DataPage中,如果里面的数据满足需求的话,就不再请求访问数据库,这样多次访问数据库的问题也解决了。
		
		
				虽然这样的话,dataScrollor的Tag使用起来还是很复杂,通常在同一个项目中,我们只会使用一种样式的分页导航,不过没关系,我们只需要修改以下DataScrollor的Render Kit,把一些可以定义的值固定下来,再定义一个TLD文件,就可以在项目中使用简化版的Tag了。
		
		
				这个方法一开始发布在Myfaces的Wiki中,http://wiki.apache.org/myfaces/WorkingWithLargeTables,那里很少有人关注到,大家有兴趣可以看看原文,本文只是对这种方法做一些简单的介绍,并非自创,希望大家能够多多关注开源社区,因为那里有最新最好的东西。
		
		
				从Nightly Build服务器中拿到的12.27的Myfaces包,发现里面扩充了很多新的Component,只是并没有正式发布,大家有兴趣的话可以研究研究。
				
		
	posted on 2005-12-30 10:30 
steady 阅读(15045) 
评论(21)  编辑  收藏  所属分类: 
JSF & Myfaces