RichFaces data driven components (rich:dataTable , rich:dataGrid etc.) can be used in more complicated scenarios unlike standard DataTable and DataModel class. To get the new supports one needs to implement his own DataModel that extends one of ExtendedDataModel classes. But it’s not efficient every time implementing the class when needs. So here I write my own solution.
Implementation
I extend org.ajax4jsf.model.SerializableDataModel class as usual. But the tricks lie in overridden walk method. A general fetch list of ExtendedFetchList is used to access data instead of a specific fetch list. ExtendedFetchList interface declares all the necessary methods for dynamic data accessing. See the following codes and usage example.
ExtendedListDataModel:
public class ExtendedListDataModel extends SerializableDataModel {
private boolean detached = false;
private BigDecimal currentPk;
private Map wrappedData = new HashMap();
private List wrappedKeys = null;
private ExtendedFetchList fetchList = null;
private int rowIndex;
private static final long serialVersionUID = 1L;
/* (non-Javadoc)
* @see org.ajax4jsf.model.SerializableDataModel#update()
*/
@Override
public void update() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.ajax4jsf.model.ExtendedDataModel#getRowKey()
*/
@Override
public Object getRowKey() {
// TODO Auto-generated method stub
return currentPk;
}
/* (non-Javadoc)
* @see org.ajax4jsf.model.ExtendedDataModel#setRowKey(java.lang.Object)
*/
@Override
public void setRowKey(Object key) {
// TODO Auto-generated method stub
this.currentPk = (BigDecimal)key;
}
/* (non-Javadoc)
* @see org.ajax4jsf.model.ExtendedDataModel#walk(javax.faces.context.FacesContext, org.ajax4jsf.model.DataVisitor, org.ajax4jsf.model.Range, java.lang.Object)
*/
@Override
public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException {
int firstRow = ((SequenceRange)range).getFirstRow();
int numberOfRows = ((SequenceRange)range).getRows();
if (detached) { // Is this serialized model
// Here we just ignore current Rage and use whatever data was saved in serialized model.
// Such approach uses much more getByPk() operations, instead of just one request by range.
// Concrete case may be different from that, so you can just load data from data provider by range.
// We are using wrappedKeys list only to preserve actual order of items.
for (BigDecimal key:wrappedKeys) {
setRowKey(key);
visitor.process(context, key, argument);
}
} else { // if not serialized, than we request data from data provider
wrappedKeys = new ArrayList();
for (Object obj:fetchList.fetchList(firstRow, numberOfRows)) {
wrappedKeys.add(fetchList.getPk(obj));
wrappedData.put(fetchList.getPk(obj), obj);
visitor.process(context, fetchList.getPk(obj), argument);
}
}
}
/* (non-Javadoc)
* @see javax.faces.model.DataModel#getRowCount()
*/
@Override
public int getRowCount() {
// TODO Auto-generated method stub
//return fetchList.getListSize();
return fetchList.getMaxListSize();
}
/* (non-Javadoc)
* @see javax.faces.model.DataModel#getRowData()
*/
@Override
public Object getRowData() {
// TODO Auto-generated method stub
if (currentPk == null) {
return null;
} else {
Object ret = wrappedData.get(currentPk);
return ret;
}
}
/* (non-Javadoc)
* @see javax.faces.model.DataModel#getRowIndex()
*/
@Override
public int getRowIndex() {
// TODO Auto-generated method stub
//throw new UnsupportedOperationException();
return rowIndex;
}
/* (non-Javadoc)
* @see javax.faces.model.DataModel#getWrappedData()
*/
@Override
public Object getWrappedData() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see javax.faces.model.DataModel#isRowAvailable()
*/
@Override
public boolean isRowAvailable() {
// TODO Auto-generated method stub
if (currentPk == null) {
return false;
} else {
return wrappedData.containsKey(currentPk);
}
}
/* (non-Javadoc)
* @see javax.faces.model.DataModel#setRowIndex(int)
*/
@Override
public void setRowIndex(int arg0) {
// TODO Auto-generated method stub
//throw new UnsupportedOperationException();
rowIndex = arg0;
return;
}
/* (non-Javadoc)
* @see javax.faces.model.DataModel#setWrappedData(java.lang.Object)
*/
@Override
public void setWrappedData(Object arg0) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
public SerializableDataModel getSerializableModel(Range range) {
if (wrappedKeys != null) {
detached = true;
// Some activity to detach persistent data from wrappedData map may be taken here.
// In that specific case we are doing nothing.
return this;
} else {
return null;
}
}
/**
* @return the fetchList
*/
public ExtendedFetchList getFetchList() {
return fetchList;
}
/**
* @param fetchList the fetchList to set
*/
public void setFetchList(ExtendedFetchList fetchList) {
this.fetchList = fetchList;
}
}
ExtendedFetchList
public interface ExtendedFetchList {
public List fetchList(int startRow, int size);
public BigDecimal getPk(Object obj);
public void update();
public int getListSize();
public int getMaxListSize();
}
Usage: An Example
AnExampleListModel:
public class AnExampleListModel implements Serializable {
private ExtendedListDataModel exampleList = new ExtendedListDataModel();
private ExtendedFetchList fetchList;
private static final long serialVersionUID = 102136548978979L;
/**
* @return the exampleList
*/
public ExtendedListDataModel getExampleList() {
return exampleList;
}
/**
* @param exampleList the exampleList to set
*/
public void setExampleList(ExtendedListDataModel exampleList) {
this.exampleList = exampleList;
}
/**
* @return the fetchList
*/
public ExtendedFetchList getFetchList() {
return fetchList;
}
/**
* @param fetchList the fetchList to set
*/
public void setFetchList(ExtendedFetchList fetchList) {
this.fetchList = fetchList;
exampleList.setFetchList(fetchList);
}
}
AnExampleFetchList:
public class AnExampleFetchList implements ExtendedFetchList, Serializable {
private static final long serialVersionUID = 1L;
private int listSize;
private int maxListSize;
public List fetchList(int startRow, int size) {
List list = new Vector();
//call some method here to populate the list
//For example
RangeQueryResult rqr = foo.getBars(startRow, size);
list = rqs.getResultList();
setListSize(list.size());
setMaxListSize(rqr.getMaxResult());
return list;
}
@SuppressWarnings("unchecked")
public BigDecimal getPk(Object obj) {
Map columns = (Map) obj;
return new BigDecimal(columns.get("id").toString());
}
public void update() {}
public int getListSize(){
return listSize;
}
public int getMaxListSize(){return maxListSize;}
/**
* @param listSize the listSize to set
*/
public void setListSize(int listSize) {
this.listSize = listSize;
}
/**
* @param maxListSize the maxListSize to set
*/
public void setMaxListSize(int maxListSize) {
this.maxListSize = maxListSize;
}
}
Bean Definitions:
<managed-bean>
<managed-bean-name>exampleListModel</managed-bean-name>
<managed-bean-class>
com.mycompany.web.model.AnExampleListModel
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>fetchList</property-name>
<property-class>
com.iecb.web.model.AnExampleFetchList
</property-class>
<value>#{exampleFetchList}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>exampleFetchList</managed-bean-name>
<managed-bean-class>
com.iecb.web.model.AnExampleFetchList
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Example Template:
<h:panelGrid id="exampleContainer" columns="1">
<rich:dataTable id="exampleListDT" value="#{exampleListModel.exampleList}" var="item" rows="25" style="width:100%">
<h:column>
<f:facet name="header">
<h:outputText value="Column1" />
</f:facet>
<h:outputText value="#{item['column1']}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Column2" />
</f:facet>
<h:outputText value="#{item['column2']}" />
</h:column>
</rich:dataTable>
<rich:spacer height="5" />
<rich:datascroller align="left" for="exampleListDT" maxPages="10" ajaxSingle="false"/>
</h:panelGrid>

5 comments
Comments feed for this article
July 27, 2009 at 8:59 pm
Kristein
could you please provide with a complete solution.
It’s really helpful for others.
thanks,
Kristein
December 27, 2008 at 12:03 am
jotadeveloper
yes please !! I need a example with hibernate, i have a table with 50,000 rows, but it’s very slow in the traditional form with (rich:datascroller).
Do you have any solution?
December 1, 2008 at 12:36 am
wandrey
Great
Can your example work with richfaces version 3,2.2 and 3.3 ?
Can you share your update solution?
September 12, 2008 at 3:22 pm
Rafael
Thanks for your article.
September 11, 2008 at 7:41 am
vimalan
Hi, Had a doubt, in the above example
what these does, no code given for it.
RangeQueryResult rqr = foo.getBars(startRow, size);
list = rqs.getResultList();
Instead of these, i written my own impl class which returns a Arraylist.
But in JSP, it is throwing classcastexception. Need help on this.
Thanks,
Vimal.