Here is a Richfaces Ajax Datascroler and Seam Example
supernovasoftware.com Dec 14, 2007 11:25 AMI use the following to view large tables where fetching the all the results is not feasible. It has about 1 million rows.
It does assume that you object implements the interface Idable shown next.
import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; import org.ajax4jsf.model.DataVisitor; import org.ajax4jsf.model.ExtendedDataModel; import org.ajax4jsf.model.Range; import org.ajax4jsf.model.SequenceRange; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Logger; import org.jboss.seam.annotations.Out; import org.jboss.seam.log.Log; import my.package.Idable; public abstract class BaseExtendedDataModel<T,ID extends Serializable> extends ExtendedDataModel implements BaseExtendedDataModelDAO<T, ID>{ private @Logger Log log; @Out(scope=ScopeType.CONVERSATION, required=false) public List<T> listRow; private ID currentId; private Map<ID, T> wrappedData = new HashMap<ID, T>(); private List<ID> wrappedKeys; private Long rowCount; // better to buffer row count locally public abstract Long getCount(); public abstract List<T> getList(Integer firstRow, Integer maxResults); public abstract T findById(ID id); public void wrap(FacesContext context, DataVisitor visitor, Range range, Object argument, List<T> list) throws IOException { wrappedKeys = new ArrayList<ID>(); wrappedData = new HashMap<ID, T>(); for (T row : list) { Idable idable = (Idable) row; ID id = (ID) idable.getId(); wrappedKeys.add(id); wrappedData.put(id, row); visitor.process(context, id, argument); } } public boolean hasById(ID id) { for (T row : listRow) { Idable idable = (Idable) row; Long rowId = (Long) idable.getId(); if (rowId.equals(id)) { return true; } } return false; } @Override public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException { int firstRow = ((SequenceRange) range).getFirstRow(); int maxResults = ((SequenceRange) range).getRows(); wrap(context, visitor, range, argument, getList(firstRow, maxResults)); } /* * This method normally called by Visitor before request Data Row. */ @Override public void setRowKey(Object key) { this.currentId = (ID) key; } @Override public int getRowCount() { if(rowCount == null) return (rowCount = this.getCount()).intValue(); else return rowCount.intValue(); } @Override public boolean isRowAvailable() { if (currentId == null) { return false; } else { return hasById(currentId); } } /** * This is main way to obtain data row. It is intensively used by framework. * We strongly recommend use of local cache in that method. */ @Override public Object getRowData() { if (currentId == null) { return null; } else { T ret = wrappedData.get(currentId); if (ret == null) { ret = this.findById(currentId); wrappedData.put(currentId, ret); return ret; } else { return ret; } } } /** * Unused rudiment from old JSF staff. */ @Override public Object getWrappedData() { throw new UnsupportedOperationException(); } @Override public int getRowIndex() { throw new UnsupportedOperationException(); } @Override public void setRowIndex(int rowIndex) { throw new UnsupportedOperationException(); } @Override public void setWrappedData(Object data) { throw new UnsupportedOperationException(); } /** * Unused update data. */ // @Override public void update() { throw new UnsupportedOperationException(); } /** * TODO if this is never called by the framework why is it necessary. */ @Override public Object getRowKey() { if (true) throw new UnsupportedOperationException(); return currentId; } } public interface BaseExtendedDataModelDAO<T, ID> { }
Idable interface
public interface Idable { public Long getId(); }
I extend the base class as shown below.
import java.util.List; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import mypackage.general.access.Access; @Name("accessExtendedDataModel") public class AccessExtendedDataModel extends BaseExtendedDataModel<Access, Long> { @In(create=true) AccessDAO accessDAO; @Override public Long getCount() { return accessDAO.getCount(); } @Override public Access findById(Long id) { return accessDAO.findById(id); } @Override public List<Access> getList(Integer firstRow, Integer maxResults) { return listRow = accessDAO.getRange(firstRow, maxResults); }
Then in the facelets page
<a4j:form> <rich:datascroller for="accessList" /> <rich:spacer height="10" /> <rich:dataTable id="accessList" value="#{accessExtendedDataModel}" var="accessL" styleClass="f_table" rowClasses="tr0,tr1" rows="30"> <rich:column id="id"> <f:facet name="header">id</f:facet> <h:outputText value="#{accessL.id}" /> </rich:column> <rich:column id="User"> <f:facet name="header">User</f:facet> <h:outputText value="#{accessL.usr.usr}" /> </rich:column> </rich:dataTable> </a4j:form>
The scroller is now connected to the datatable. This is a first pass and was adapted for an example in the Richfaces cvs I took a while back.
The paging is all done with Ajax and only 30 objects are loaded at a time.
Any suggestions for improvement are most welcome.