-
1. Re: Data scroller backed by EntityQuery
vk101 Sep 23, 2008 11:44 PM (in response to vk101)It seems that neilac333 from the old forum solved the problem (see the post: Page 1 and Page 2)
Does anyone know what became of this - that person was going to post to the knowledgebase or on the seamframework.org site, but I haven't been able to find it? This could solve my problem completely - it's exactly what I'm running into.
-
2. Re: Data scroller backed by EntityQuery
red247.matt.parker.cox.net Sep 24, 2008 10:32 PM (in response to vk101)Here is a piece of code that works for me. It is based on several examples that I found on-line.
1. Eclectic Programmer Example
2. Richfaces Datascroler, EntityQuery and Pagination
3. Implementing Richfaces Extended Data Model Classes
I deleted some of the mechanics that pertain to my specific application. I created an abstract class for most of the implementation since I have a couple of data tables.
Although there is some code to track the sorting field, I don't really using it within the Hibernate queries that run in the background.First you need a class that extends org.ajax4jsf.model.SerializableDataModel.
public abstract class ViewDataTableModel extends SerializableDataModel { @Logger private Log log; /** The total number of data rows in the current view. **/ protected Long totalRows = null; /** Field to sort the data. **/ protected String sortField = "id"; /** Sorting direction related to the sorted field. **/ protected boolean descending = false; /** The user's position in the dataset . **/ protected int rowIndex; /** The first row displayed in the table. **/ protected int firstRow = -1; protected List<Long> wrappedKeys = null; /** * Default constructor. */ public ViewDataTableModel(){ super(); } /* (non-Javadoc) * @see org.ajax4jsf.model.ExtendedDataModel#getSerializableModel(org.ajax4jsf.model.Range) */ @Override public SerializableDataModel getSerializableModel(Range range) { if ( wrappedKeys != null ){ detached = true; return this; } return null; } /** * * @return */ protected Object getSortFieldObject() { FacesContext context = FacesContext.getCurrentInstance(); Object sortFieldObject = context.getExternalContext().getRequestParameterMap().get("sortField"); return sortFieldObject; } /* (non-Javadoc) * @see org.ajax4jsf.model.SerializableDataModel#update() */ @Override public void update() { log.info("Updating RawDataTableModel"); if (getSortFieldObject() != null){ String newSortField = getSortFieldObject().toString(); if (newSortField.equals(sortField)){ descending = !descending; } sortField = newSortField; } detached = true; } /** * Not supported. */ @Override public Object getWrappedData() { throw new UnsupportedOperationException(); } /** * Not supported. */ @Override public void setWrappedData(Object arg0) { throw new UnsupportedOperationException(); } /* (non-Javadoc) * @see org.ajax4jsf.model.ExtendedDataModel#getRowKey() */ @Override public Object getRowKey() { return currentPK; } /* (non-Javadoc) * @see org.ajax4jsf.model.ExtendedDataModel#setRowKey(java.lang.Object) */ @Override public void setRowKey(Object object) { currentPK = (Long)object; } /* (non-Javadoc) * @see javax.faces.model.DataModel#getRowIndex() */ @Override public int getRowIndex() { log.info("Returning row index. " + rowIndex ); return rowIndex; } /* (non-Javadoc) * @see javax.faces.model.DataModel#setRowIndex(int) */ @Override public void setRowIndex(int rowIndex) { this.rowIndex = rowIndex; } /* (non-Javadoc) * @see javax.faces.model.DataModel#getRowCount() */ @Override public int getRowCount() { if ( totalRows == null ){ log.info("Updating row count first...."); updateRowCount(); } log.info("Row count " + totalRows ); return totalRows.intValue(); } /** * Returns the number of data rows in the current view. */ protected abstract void updateRowCount(); }
Here is the main implementation that interfaces with Hibernate. ConvolveUI is an entity bean.
@Name("convolveTableModel") @Scope(ScopeType.SESSION) public class ConvolveTableModel extends ViewDataTableModel { private static final long serialVersionUID = -5268176093221597456L; @Logger Log log; @In ConvolveDAOLocal convolveDAO; private final Map<Long, ConvolveUI> wrappedData = new HashMap<Long, ConvolveUI>(); /** * Default constructor. */ public ConvolveTableModel() { super(); } /* (non-Javadoc) * @see com.itt.bap.browser.datamodel.ViewDataTableModel#updateRowCount() */ @Override protected void updateRowCount() { totalRows = convolveDAO.getTotalRows(display.getLeft(), display.getRight(), display.getTop(), display.getBottom(), display.getStartDate(), display.getEndDate() ); log.info( totalRows + " convovlved areas retrieved."); } /* (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 { log.info("walking data model."); boolean viewChanged = this.hasViewChanged(); int first = ((SequenceRange)range).getFirstRow(); int totalRows = ((SequenceRange)range).getRows(); if ( detached && !viewChanged && (firstRow == first) ){ for (Long key: wrappedKeys ){ setRowKey( key ); visitor.process(context, key, argument); } } else { log.info("Requerying data from database. First record: " + first + " Total rows: " + totalRows ); wrappedKeys = new ArrayList<Long>(); wrappedData.clear(); //Retrieve data using Hibernate. Your implmementation here. List<Convolve> temp = convolveDAO.findData(); Iterator<Convolve> rows = temp.iterator(); while ( rows.hasNext() ){ Convolve row = rows.next(); row.getUsers(); ConvolveUI convolveUI = new ConvolveUI( row, user ); wrappedKeys.add( new Long( row.getId() ) ); wrappedData.put( new Long( row.getId() ), convolveUI ); visitor.process(context, new Long( row.getId() ), argument ); } if ( viewChanged ){ updateRowCount(); } firstRow = first; } } /* (non-Javadoc) * @see javax.faces.model.DataModel#getRowData() */ @Override public Object getRowData() { if ( currentPK == null ) return null; log.info("Getting convolve area " + currentPK.toString() ); return (ConvolveUI)wrappedData.get( currentPK ); } /* (non-Javadoc) * @see javax.faces.model.DataModel#isRowAvailable() */ @Override public boolean isRowAvailable() { boolean available = false; if ( currentPK == null ){ return false; } else if ( wrappedKeys.contains( currentPK ) ){ return true; } else if ( wrappedData.entrySet().contains( currentPK ) ){ return true; } log.info("Convolve area available: " + available ); return available; } public ConvolveUI getWrappedDataItem( Long key ){ if ( wrappedData.containsKey( key ) ){ return wrappedData.get( key ); } return null; } /** * Delete the bean. */ @Destroy @Remove public void destroy(){ System.out.println("Destorying ConvolveTableModel"); wrappedData.clear(); wrappedKeys.clear(); } }
<rich:tab name="locationConvolvedTab" label="Convolved" switchType="ajax" actionListener="#{locationConvolveTab.select}" immediate="false"> <a4j:form> <rich:dataTable id="convolveDataTable" value="#{convolveTableModel}" var="convolved" rows="10" sortMode="single"> <rich:column> <!-- Your column definition here --> </rich:column> <f:facet name="footer"> <rich:datascroller for="convolveDataTable" maxPages="10" pageIndexVar="pageIndex" pagesVar="pages"/> </f:facet> </rich:dataTable> </a4j:form> </rich:tab>
Hopefully this helps...
-
3. Re: Data scroller backed by EntityQuery
vk101 Sep 28, 2008 10:17 AM (in response to vk101)Thanks for the code. I tried using it, and I have a couple of questions - where are pageIndexVar="pageIndex" and pagesVar="pages" used? (i.e. where and how do
pageIndex
andpages
get set, so that the dataScroller to make use of these values?)When you click the forward or back button of the dataScroller, how do these values get sent to the backing data model for use?
Also, getRows() and getFirstRow() of Range always seem to return 0. Where and how are these properties set? The issue I'm having is that the previous/next buttons aren't selectable, and no page numbers are appearing on the dataScroller - I imagine it's a Range object issue?
Hopefully you could please clarify how to properly attempt to use your code samples. Thanks so much.
-
4. Re: Data scroller backed by EntityQuery
andygibson.contact.andygibson.net Oct 3, 2008 6:29 AM (in response to vk101)I saw this post the other day and came back to provide a link to a blog post I just finished describing how to write reusable data table pagination and ordering backed by EntityQuery objects without having to re-write everything or use anything other than standard JSF controls (no trinidad client side sorting). I also tried to stick to the standard Seam EntityQuery to keep things compatible if you need to retro-fit it. It solves the problems of SQL injection on the order by clause and handles multiple queries on a page without having to define parameters for each query and includes ajax based ordering and pagination.
Codeless ajax ordered paginated tables post
Cheers,
Andy Gibson
-
5. Re: Data scroller backed by EntityQuery
ngagov Dec 11, 2008 4:59 PM (in response to vk101)This example is working for me. It is based on Dynamic Entity Queries that could be defined also in components.xml:
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.el.Expression;
import javax.faces.context.FacesContext;
import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.Range;
import org.ajax4jsf.model.SequenceRange;
import org.ajax4jsf.model.SerializableDataModel;
import org.jboss.seam.framework.EntityQuery;
import org.richfaces.model.ExtendedFilterField;
import org.richfaces.model.FilterField;
import org.richfaces.model.Modifiable;
import org.richfaces.model.Ordering;
import org.richfaces.model.SortField2;
import com.bprocess.bmv.tools.synchro.ReflectionUtils;
/**
* Generic Pagination Data Model that fetches from database only the necessary data displayed per page
*
* @author Nikolai Gagov (08.12.2008)
*
* <br><b>History:</b> <br>
* 08.12.2008 Nikolai Gagov created <br>
* @param <T>
*/
public abstract class PaginatingDataModel<T> extends SerializableDataModel implements Modifiable {
private int rowCount = 0;
private boolean isDetached = false;
private String sortFieldName;
private Ordering ordering;
private Serializable currentPk;
private final List<Serializable> wrappedKeys = new LinkedList<Serializable>();
private final Map<Serializable, T> wrappedData = new HashMap<Serializable, T>();
private final Map<String, String> filterMap = new HashMap<String, String>();
public abstract EntityQuery<T> getEntityQuery();
public abstract T getObjectById(Serializable id);
public abstract List<String> getRestrictions();
@Override
public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException {
if (isDetached) {
for (Serializable key : wrappedKeys) {
setRowKey(key);
visitor.process(context, key, argument);
}
} else {
final int firstRow = ((SequenceRange) range).getFirstRow();
final int numberOfRows = ((SequenceRange) range).getRows();
wrappedKeys.clear();
wrappedData.clear();
final List<T> objects = loadData(firstRow, numberOfRows, sortFieldName, ordering);
for (T object : objects) {
final Serializable id = getId(object);
wrappedKeys.add(id);
wrappedData.put(id, object);
visitor.process(context, id, argument);
}
}
}
@Override
public SerializableDataModel getSerializableModel(Range range) {
isDetached = true;
rowCount = 0;
return this;
}
@Override
public void update() {
isDetached = false;
filterMap.clear();
sortFieldName = null;
}
@Override
public int getRowCount() {
if (rowCount == 0) {
rowCount = countRecords();
}
return rowCount;
}
@Override
public Object getRowData() {
if (currentPk == null) {
return null;
} else {
T object = wrappedData.get(currentPk);
if (object == null) {
object = getObjectById(currentPk);
wrappedData.put(currentPk, object);
}
return object;
}
}
@Override
public boolean isRowAvailable() {
if (currentPk == null) {
return false;
}
if (wrappedKeys.contains(currentPk)) {
return true;
}
if (wrappedData.entrySet().contains(currentPk)) {
return true;
}
if (getObjectById(currentPk) != null) {
return true;
}
return false;
}
@Override
public void modify(List<FilterField> filterFields, List<SortField2> sortFields) {
isDetached = false;
filterMap.clear();
if (!filterFields.isEmpty()) {
for (FilterField filterField : filterFields) {
final ExtendedFilterField extendedFilterField = (ExtendedFilterField) filterField;
final String filterValue = extendedFilterField.getFilterValue();
if (filterValue != null && filterValue.length() > 0) {
final String expressionString = extendedFilterField.getExpression().getExpressionString();
final String filterFieldName = getFilterExpressionName(expressionString);
filterMap.put(filterFieldName, filterValue);
}
}
}
if (!sortFields.isEmpty()) {
final SortField2 sortFild = sortFields.get(0);
final Expression expression = sortFild.getExpression();
final String expressionString = expression.getExpressionString();
ordering = sortFild.getOrdering();
sortFieldName = getExpressionName(expressionString);
}
}
@Override
public Object getRowKey() {
return currentPk;
}
@Override
public void setRowKey(final Object key) {
this.currentPk = (Serializable) key;
}
@Override
public void setRowIndex(int index) {}
@Override
public int getRowIndex() {
throw new UnsupportedOperationException();
}
@Override
public Object getWrappedData() {
throw new UnsupportedOperationException();
}
@Override
public void setWrappedData(Object data) {
throw new UnsupportedOperationException();
}
private String getExpressionName(String expressionString) {
return expressionString.substring(expressionString.indexOf('.') + 1, expressionString.indexOf('}'));
}
private String getFilterExpressionName(String expressionString) {
return expressionString.substring(expressionString.indexOf('{') + 1, expressionString.indexOf('}'));
}
private void addFilters(EntityQuery<T> query) {
query.setRestrictions(new LinkedList<String>(getRestrictions()));
final Set<String> filterFields = filterMap.keySet();
for (String filterFieldName : filterFields) {
final String filterValue = filterMap.get(filterFieldName);
final String restrictionToRemove = filterFieldName + " LIKE ";// TODO test recursive removal without LIKE clause
if (filterFieldName.indexOf(".") > 0) {
filterFieldName = filterFieldName.substring(filterFieldName.lastIndexOf(".") + 1);
}
final String newRestriction = restrictionToRemove + "#{" + filterFieldName + "}||''";
org.jboss.seam.contexts.Contexts.getEventContext().set(filterFieldName, filterValue);
removeRestriction(restrictionToRemove);
final List<String> oldRestrictions = query.getRestrictions();
oldRestrictions.add(newRestriction);
query.setRestrictions(oldRestrictions);
}
}
private void addSort(EntityQuery<T> query, String sortField, Ordering ordering) {
if (sortField != null) {
if (ordering.equals(Ordering.DESCENDING)) {
query.setOrder(sortField + " desc ");
} else {
query.setOrder(sortField + " asc ");
}
}
}
private List<T> loadData(int firstRow, int numberOfRows, String sortField, Ordering ordering) {
final EntityQuery<T> query = getEntityQuery();
addFilters(query);
addSort(query, sortField, ordering);
query.setFirstResult(firstRow);
query.setMaxResults(numberOfRows);
final List<T> objects = query.getResultList();
return objects;
}
private Serializable getId(T object) {
final Serializable id = ReflectionUtils.getPrimaryKey(object);
return id;
}
private int countRecords() {
final EntityQuery<T> query = getEntityQuery();
addFilters(query);
final int containerCount = query.getResultCount().intValue();
return containerCount;
}
private void removeRestriction(String restrictionToRemove) {
final EntityQuery<T> query = getEntityQuery();
final List<String> restrictions = query.getRestrictions();
String foundRestriction = null;
for (String restriction : restrictions) {
if (restriction.startsWith(restrictionToRemove)) {
foundRestriction = restriction;
break;
}
}
if (foundRestriction != null) {
restrictions.remove(foundRestriction);
query.setRestrictions(restrictions);
}
}
}
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.EntityManager;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.framework.EntityQuery;
import com.bprocess.bmv.domain.bm4.BpsUser;
/**
* Custom pagination data model for Users screen
*
* @author Nikolai Gagov (11.12.2008)
*
* <br><b>History:</b> <br>
* 11.12.2008 Nikolai Gagov created <br>
*/
@Name("usersPaginatingDataModel")
@Scope(ScopeType.CONVERSATION)
public class UsersPaginatingDataModel extends PaginatingDataModel<BpsUser> {
private static final long serialVersionUID = 1L;
@In
private EntityManager entityManager;
@In(value="#{allPagePartyUsers}")
private EntityQuery<BpsUser> allPagePartyUsers;
private List<String> restrictions = new LinkedList<String>();
@Create
public void startup() {
restrictions.addAll(allPagePartyUsers.getRestrictions());
}
@Override
public EntityQuery<BpsUser> getEntityQuery() {
return allPagePartyUsers;
}
@Override
public BpsUser getObjectById(Serializable id) {
return entityManager.find(BpsUser.class, id);
}
@Override
public List<String> getRestrictions() {
return restrictions;
}
}
components.xml:
<framework:entity-query name="partnersQuery"
ejbql="SELECT DISTINCT comp
FROM BpsUvrt uvrt
JOIN uvrt.id.bpsComp comp
JOIN uvrt.id.bpsUser user
WHERE user = #{user}"
order="comp.compName" />
users.xhtml:
<rich:dataTable id="objectList" value="#{usersPaginatingDataModel}" var="user"
rows="#{paginaterPageSize}" reRender="paginationBar">
.....
<s:div id="paginationBar">
<a4j:form styleClass="paginationBarClass">
<rich:datascroller styleClass="paginationBarLinkClass" for="objectList"
maxPages="5" boundaryControls="auto" fastControls="auto" />
</a4j:form>
</s:div> -
6. Re: Data scroller backed by EntityQuery
deanhiller2000 Feb 18, 2010 9:33 PM (in response to vk101)What scope is everyone using...EVENT I assume so it clears the row count out in case it changes(I mean after the count sql, you run your real sql and you don't want the reall sql trying to page somewhere with no data).
ie. if you are in conversation or session, and have 50 rows(50 count), but then user clicks to last page and there is only 40 rows now, the user would run over and count is still 50.
I changed to event but am having trouble...my sequenceRange.getFirstRow started returning 0 every time...it used to return the right values....it still does however get the number of pages to display correctly for some odd reason. I cahnge to conversation and still getFristRow is 0 though so I am not sure what is going on here...any ideas?
thanks,
Dean -
7. Re: Data scroller backed by EntityQuery
deanhiller2000 Feb 19, 2010 12:43 AM (in response to vk101)well, that didn't work, needed the datamodel to be in the conversation instead otherwise, every time user chose a row, it reran the query so Im moved datamodel to conversation and outjected the cachedCount as EVENT scope so it would not be cached for entire conversation. Unfortunately, the dang model is calling getRowCount every time for some damn reason when user makes a selection which I don't want either.
hmmmmm, this is a pain to get right. maybe I should just stick with the bug everyone else has where the cachednumber of items is false over time as the user keeps stepping through the data....but I don't like that too much.