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.