SerializableDataModel.walk range problem
afrontczak Mar 16, 2010 4:53 AMI want to implement ExtendedD
ataGrid with DataScroller with
loading data from DB only for current page. I need also to use implemented in the grid sorting
and external filtering.
Unfortunatelly I couldn't find any example of such scenario. I found only examples with data taken from internal array instead
of partially loading from DB.
I implemented the solution according to example from Richfaces Demo - so I used SerializedDataModel and DataProvider.
I encountered following problems:
1) loading/reloading of the dataTable - dataModel.walk method is invoked 6 times, the first 4 invocations have Range set properly
with first row and number of rows, but the last two have always Range from 0, and nr or rows=-1 - so all rows are read from DB instead of
only proper range.
2) page change on DataScroller - dataModel.walk method is invoked 2 times, Range from 0, and nr or rows=-1 - so all rows are read from DB instead of
only proper range.
3) sorting by any sortable column results in properly sorted whole set of items, but the table behaves the same as during loading/reloading - 6 invocations of dataModel.walk and whole set of items read
Questions:
1) Am I doing something wrong with dataLoading for dataTable page?
2) How to get sort params from dataTable to pass them to DB query?
Details of my implementation:
1) JBoss 4.2.3, Richfaces 3.3.3CR1 (also tested on 3.3.2SR1)
2) DataModel and DataProvider stored in statefull session bean
DataModel, DataProvider and part of xhtml page sources:
#############################################################################
public class TestDataModel extends SerializableDataModel {
private TestDataProvider dataProvider;
private Integer currentPk;
private Map<Integer,TestDbStoredItem> wrappedData = new HashMap<Integer,TestDbStoredItem>();
private List<Integer> wrappedKeys = null;
private static final long serialVersionUID = -1956179896877538628L;
@Override
public Object getRowKey() {
return currentPk;
}
@Override
public void setRowKey(Object key) {
this.currentPk = (Integer) key;
}
@Override
public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException {
int firstRow = ((SequenceRange)range).getFirstRow();
int numberOfRows = ((SequenceRange)range).getRows();
wrappedKeys = new ArrayList<Integer>();
for (TestDbStoredItem item:dataProvider.getItemsByrange(new Integer(firstRow), numberOfRows, null, true)) {
wrappedKeys.add(item.getPk());
wrappedData.put(item.getPk(), item);
visitor.process(context, item.getPk(), argument);
}
}
@Override
public int getRowCount() {
return getDataProvider().getRowCount();
}
@Override
public Object getRowData() {
if (currentPk==null) {
return null;
} else {
TestDbStoredItem ret = wrappedData.get(currentPk);
if (ret==null) {
ret = getDataProvider().getItemByKey(currentPk);
wrappedData.put(currentPk, ret);
return ret;
} else {
return ret;
}
}
}
@Override
public int getRowIndex() {
throw new UnsupportedOperationException();
}
@Override
public Object getWrappedData() {
throw new UnsupportedOperationException();
}
@Override
public boolean isRowAvailable() {
if (currentPk==null) {
return false;
} else {
return getDataProvider().hasEventByPk(currentPk);
}
}
@Override
public void setRowIndex(int rowIndex) {
//ignore
}
@Override
public void setWrappedData(Object data) {
throw new UnsupportedOperationException();
}
public SerializableDataModel getSerializableModel(Range range) {
if (wrappedKeys!=null) {
return this;
} else {
return null;
}
}
public TestDataProvider getDataProvider() {
return dataProvider;
}
public void setDataProvider(TestDataProvider dataProvider) {
this.dataProvider = dataProvider;
}
@Override
public void update() {
//nothing to do due in readonly datamodel
}
}
#############################################################################
public class TestDataProvider implements DataProvider<TestDbStoredItem> {
private static final long serialVersionUID = -4888516018080762650L;
private List<TestDbStoredItem> wrappedtems = new ArrayList<TestDbStoredItem>();
private TestStatefulBean testStatefullBean;
public EventDataProvider(TestStatefulBean testStatefullBean) {
this.testStatefullBean = testStatefullBean;
}
public boolean hasEventByPk(Integer pk) {
for (TestDbStoredItem item : wrappedtems) {
if (item.getPk().equals(pk)) {
return true;
}
}
return false;
}
public List<TestDbStoredItem> getItemsByrange(Integer startPk,
int numberOfRows, String sortField, boolean ascending) {
wrappedtems = testStatefullBean.getDbStoredItemsByRange(startPk,
numberOfRows, sortField, ascending);
return wrappedtems;
}
public void update() {
// nothing need to do
}
public int getRowCount() {
return testStatefullBean.getNumberOfFilteredEvents();
}
@Override
public TestDbStoredItem getItemByKey(Object key) {
TestDbStoredItem item = null;
if (key != null) {
for (TestDbStoredItem itemTmp : wrappedtems) {
if (itemTmp.getPk().equals(key)) {
item = itemTmp;
}
}
if (item == null) {
item = testStatefullBean
.getDbStoredItemByPK(Integer.parseInt(key.toString()));
}
}
return item;
}
@Override
public List<TestDbStoredItem> getItemsByRange(int firstRow, int endRow) {
//not used
return new ArrayList<TestDbStoredItem>();
}
@Override
public Object getKey(TestDbStoredItem item) {
return item.getPk();
}
}
#############################################################################
<rich:extendedDataTable id="sequencesTable" var="item"
rowKeyVar="rkv"
value="#{backing_guiListVotingSeq.eventDataModel}"
tableState="#{backing_guiListVotingSeq.sequencesTableState}"
selection="#{backing_guiListVotingSeq.activeSequenceSelection}"
selectionMode="single" width="870px" height="385px" rows="12">
<a4j:support event="ondblclick" ajaxSingle="true"
process="sequencesTable"
action="#{backing_guiListVotingSeq.sequenceRowDblClick_action}"
eventsQueue="ajaxEventQueue" ignoreDupResponses="true"
requestDelay="500" />
<a4j:support event="onselectionchange" ajaxSingle="true"
actionListener="#{backing_guiListVotingSeq.activeSequenceSelectionChanged}"
process="sequencesTable" reRender="messagesTable,locations"
eventsQueue="ajaxEventQueue" ignoreDupResponses="true"
requestDelay="500" />
<rich:column sortable="false" label="" width="50px">
<h:selectBooleanCheckbox value="#{item.selected}"
onclick="checkBoxClick(this,#{item.pk})" />
</rich:column>
<rich:column sortable="true" sortBy="#{item.pk}"
sortOrder="#{backing_guiListVotingSeq.tableVotingsOrderingId}"
label="#{extMsg['voting.gui.id']}" width="75px">
<f:facet name="header">
<h:outputText value="#{extMsg['voting.gui.id']}" />
</f:facet>
<h:outputText value="#{item.pk}" />
</rich:column>
<rich:column sortable="true" sortBy="#{item.event.name}"
sortOrder="#{backing_guiListVotingSeq.tableVotingsOrderingName}"
label="#{msg['voting.VotingSequence.name']}" width="350px">
<f:facet name="header">
<h:outputText value="#{msg['voting.VotingSequence.name']}" />
</f:facet>
<h:outputText value="#{item.event.name}"
styleClass="normalWrappedText" />
</rich:column>
<f:facet name="footer">
<rich:datascroller id="seqTableDatascroller" align="left"
maxPages="20"
page="#{backing_guiListVotingSeq.eventTableScrollerPage}"
reRender="messagesTable,locations"
actionListener="#{backing_guiListVotingSeq.sequencesTablePageChanged}" />
</f:facet>
</rich:extendedDataTable>