You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by Apache Wiki <wi...@apache.org> on 2010/06/02 05:15:48 UTC

[Myfaces Wiki] Update of "WorkingWithLargeTables" by MarkRicard

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Myfaces Wiki" for change notification.

The "WorkingWithLargeTables" page has been changed by MarkRicard.
The comment on this change is: PagedListDataModel's fetchPage could be called multiple times in a row for the same startRow when it is not needed and the code in fetchPage could be very expensive.   .
http://wiki.apache.org/myfaces/WorkingWithLargeTables?action=diff&rev1=10&rev2=11

--------------------------------------------------

   */
  public abstract class PagedListDataModel<T> extends DataModel {
  
-     int pageSize;
-     int rowIndex;
+ 	private int pageSize;
+ 	private int rowIndex;
-     DataPage<T> page;
+ 	private DataPage<T> page;
-     
-     /*
+ 	
+ 	private int lastStartRow = -1;
+ 	private DataPage<T> lastPage;
+ 
+ 	/*
-      * 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
+ 	 * Not used in this class; data is fetched via a callback to the fetchData
-      * fetchData method rather than by explicitly assigning a list.
+ 	 * method rather than by explicitly assigning a list.
-      */
+ 	 */
-     @Override
+ 	@Override
-     public void setWrappedData(Object o) {
+ 	public void setWrappedData(Object o) {
-         throw new UnsupportedOperationException("setWrappedData");
+ 		throw new UnsupportedOperationException("setWrappedData");
-     }
+ 	}
  
-     @Override
+ 	@Override
-     public int getRowIndex() {
+ 	public int getRowIndex() {
-         return rowIndex;
+ 		return rowIndex;
-     }
+ 	}
  
-     /**
+ 	/**
-      * Specify what the "current row" within the dataset is. Note that
+ 	 * Specify what the "current row" within the dataset is. Note that the
-      * the UIData component will repeatedly call this method followed
+ 	 * UIData component will repeatedly call this method followed by getRowData
-      * by getRowData to obtain the objects to render in the table.
+ 	 * to obtain the objects to render in the table.
-      */
+ 	 */
-     @Override
+ 	@Override
-     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
+ 	 * Return the total number of rows of data available (not just the number of
-      * number of rows in the current page!).
+ 	 * rows in the current page!).
-      */
+ 	 */
-     @Override
+ 	@Override
-     public int getRowCount() {
+ 	public int getRowCount() {
-         return getPage().getDatasetSize();
+ 		return getPage().getDatasetSize();
-     }
-     
-     /**
+ 	}
+ 
+ 	/**
-      * Return a DataPage object; if one is not currently available then
+ 	 * Return a DataPage object; if one is not currently available then fetch
-      * fetch one. Note that this doesn't ensure that the datapage
+ 	 * one. Note that this doesn't ensure that the datapage returned includes
-      * returned includes the current rowIndex row; see getRowData.
+ 	 * the current rowIndex row; see getRowData.
-      */
+ 	 */
-     private DataPage<T> getPage() {
+ 	private DataPage<T> 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 = suggestFetchPage(startRow, pageSize);
-         return page;
-     }
+ 		return page;
+ 	}
  
-     /**
+ 	/**
-      * Return the object corresponding to the current rowIndex.
+ 	 * Return the object corresponding to the current rowIndex. If the DataPage
-      * If the DataPage object currently cached doesn't include that
-      * index then fetchPage is called to retrieve the appropriate page.
-      */
+ 	 * object currently cached doesn't include that index then fetchPage is
+ 	 * called to retrieve the appropriate page.
+ 	 */
-     @Override
+ 	@Override
-     public Object getRowData(){
+ 	public Object getRowData() {
-         if (rowIndex < 0) {
+ 		if (rowIndex < 0) {
+ 			throw new IllegalArgumentException("Invalid rowIndex for PagedListDataModel; not within page");
+ 		}
-             throw new IllegalArgumentException(
-                 "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 = suggestFetchPage(rowIndex, pageSize);
-         }
+ 		}
  
-         // Check if rowIndex is equal to startRow,
+ 		// Check if rowIndex is equal to startRow,
-         // useful for dynamic sorting on pages
+ 		// useful for dynamic sorting on pages
+ 
+ 		if (rowIndex == page.getStartRow()) {
+ 			page = suggestFetchPage(rowIndex, pageSize);
+ 		}
+ 
+ 		int datasetSize = page.getDatasetSize();
+ 		int startRow = page.getStartRow();
+ 		int nRows = page.getData().size();
+ 		int endRow = startRow + nRows;
+ 
+ 		if (rowIndex >= datasetSize) {
+ 			throw new IllegalArgumentException("Invalid rowIndex");
+ 		}
+ 
+ 		if (rowIndex < startRow) {
+ 			page = suggestFetchPage(rowIndex, pageSize);
+ 			startRow = page.getStartRow();
+ 		}
+ 		else if (rowIndex >= endRow) {
+ 			page = suggestFetchPage(rowIndex, pageSize);
+ 			startRow = page.getStartRow();
+ 		}
+ 
+ 		return page.getData().get(rowIndex - startRow);
+ 	}
+ 
+ 	@Override
+ 	public Object getWrappedData() {
+ 		return page.getData();
+ 	}
+ 
+ 	/**
+ 	 * 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
+ 	 * not in the currently cached DataPage; if so then when getRowData is
+ 	 * called the required DataPage will be fetched by calling fetchData.
+ 	 */
+ 	@Override
+ 	public boolean isRowAvailable() {
+ 		DataPage<T> page = getPage();
+ 		if (page == null)
+ 			return false;
+ 
+ 		int rowIndex = getRowIndex();
+ 		if (rowIndex < 0) {
+ 			return false;
+ 		}
+ 		else if (rowIndex >= page.getDatasetSize()) {
+ 			return false;
+ 		}
+ 		else {
+ 			return true;
+ 		}
+ 	}
+ 
+ 	/**
+ 	 * the jsf framework can be funky.  It could ask for the same start row multiple times within the
+ 	 * same cycle.   Therefore, we cache the results for the startRow and make sure that if the framework
+ 	 * asks for them again in very short order that we simply returned the cached value.   What the
+ 	 * extended class could do in fetchPage could be costly, so we make sure it is called only when needed!
+ 	 * 
+ 	 * @param startRow
+ 	 * @param pageSize
+ 	 * @return
+ 	 */
+ 	public DataPage<T> suggestFetchPage(int startRow, int pageSize) {
+ 		if(this.lastStartRow == startRow) {
+ 			return this.lastPage;
+ 		}
  		
- 	if (rowIndex == page.getStartRow()){
+ 		this.lastStartRow = startRow;
- 		page = fetchPage(rowIndex, pageSize);
+ 		this.lastPage = fetchPage(startRow, pageSize);
-         }
+ 		return this.lastPage;
+ 	}
  
+ 	/**
-         int datasetSize = page.getDatasetSize();
-         int startRow = page.getStartRow();
-         int nRows = page.getData().size();
-         int endRow = startRow + nRows;
-         
-         if (rowIndex >= datasetSize) {
-             throw new IllegalArgumentException("Invalid rowIndex");
-         }
-         
-         if (rowIndex < startRow) {
-             page = fetchPage(rowIndex, pageSize);
-             startRow = page.getStartRow();
-         } else if (rowIndex >= endRow) {
-             page = fetchPage(rowIndex, pageSize);
-             startRow = page.getStartRow();
-         }
-         
-         return page.getData().get(rowIndex - startRow);
-     }
- 
-     @Override
-     public Object getWrappedData() {
-         return page.getData();
-     }
- 
-     /**
-      * 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 not in the currently cached 
-      * DataPage; if so then when getRowData is called the
-      * required DataPage will be fetched by calling fetchData.
-      */
-     @Override
-     public boolean isRowAvailable() {
-         DataPage<T> page = getPage();
-         if (page == null)
-             return false;
-         
-         int rowIndex = getRowIndex();
-         if (rowIndex < 0) {
-             return false;
-         } else if (rowIndex >= page.getDatasetSize()) {
-             return false;
-         } else {
-             return true;
-         }
-     }
- 
-     /**
-      * Method which must be implemented in cooperation with the
+ 	 * Method which must be implemented in cooperation with the managed bean
-      * managed bean class to fetch data on demand.
+ 	 * class to fetch data on demand.
-      */
+ 	 */
-     public abstract DataPage<T> fetchPage(int startRow, int pageSize);
+ 	public abstract DataPage<T> fetchPage(int startRow, int pageSize);
  }
- }}}
  
  
  Finally, the managed bean needs to provide a simple inner class that provides the fetchPage implementation: