11 Replies Latest reply on Mar 1, 2011 4:45 PM by danielk

    Displaying total rowCount of a dataTable

    cmathrusse

      I'm using RichFaces 3.3.1-GA.

      I have a dataTable defined and the columns allow for filtering. I would like to show a total number of rows that are visible in the table. For example: when the user performs the search all matching rows are displayed, (paginated with a rich:dataSroller) At the bottom of the table I would like to display the total rows returned. When the user then filters the data, using rich:column filterBy, I'd like to display the matching rows that are displayed.

      Is there an easy way to retrieve the row count from the dataTable or any of the other components?

      Thanks....

        • 1. Re: Displaying total rowCount of a dataTable
          ilya_shaikovsky

          How about just to retrieve it from model?

          • 2. Re: Displaying total rowCount of a dataTable
            cmathrusse

            That would only work on the initial display of the table. When filtering is performed, based on filterBy, the model would not get updated with the matching row count.

            • 3. Re: Displaying total rowCount of a dataTable
              ilya_shaikovsky
              • 4. Displaying total sums in the footer of a dataTable
                kado

                Hi,

                 

                sorry for dig up an old thread, but I've the same problem as Chris (had). We are using JEE 6 with JPA and Richfaces 3.3.3 and I don't know how to implement my own data model described in the example. And I also had no idea how to use the own data model to solve this problem.

                 

                I calculate the sums of some columns and give it out in the footer. But when I use a filter, the footer will not updated at this time and the sum is the same as before.

                 

                It would be very nice if someone can help me, because it is one of the main characteristics of the application.

                 

                Thanks in advance.

                 

                Greetings, Jan

                • 5. Re: Displaying total sums in the footer of a dataTable
                  kado

                  I've rewritten the BaseModifiableHibernateDataModel from the example to a JPA compatible one, with JPA Criteria API. But I've another problems now:

                   

                  Problem #1 - Performance: The performance is unacceptable, cause of the many database queries, which are triggered by walk() method. Can somebody tell me why the walk() and modify() method is called so often although the filter is typed once again?

                   

                  Problem #2 - Call sequence: The first method which will be called by typing the filter, is walk(). After that modify() is called. When I tried to get the list of items in my DisplayBean, I can't be sure that walk() is called before I got the items. I probably get the items from an old query which doesn't represent my actual displayed data.

                   

                  So, please try to explain how to solve my inital problem with an own data model?

                  • 6. Re: Displaying total sums in the footer of a dataTable
                    ilya_shaikovsky

                    1) so you should just avoid calling data from db every time it's called by component. It occurs a few times during lifecycle according to jsf spec.

                     

                    2) not sure that get the point.. if you performing some calls to dataMode in your actions/listeners - data already populated to the model(at update model phase) - so you should get with the new data already.

                    • 7. Re: Displaying total sums in the footer of a dataTable
                      kado

                      So, I hope it will be clearer with my example code (only for testing my problem):

                       

                      test.xhtml:

                      <?xml version="1.0" encoding="ISO-8859-1"?>
                      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                      <html xmlns="http://www.w3.org/1999/xhtml"
                          xmlns:ui="http://java.sun.com/jsf/facelets"
                          xmlns:f="http://java.sun.com/jsf/core"
                          xmlns:h="http://java.sun.com/jsf/html"
                          xmlns:a4j="http://richfaces.org/a4j"
                          xmlns:rich="http://richfaces.org/rich">
                      
                      <ui:composition template="/templates/standard-layout.xhtml">
                          <ui:define name="title">Test</ui:define>
                          <ui:define name="caption">Test</ui:define>
                          <ui:define name="body">
                              <h:form>
                                  <rich:dataTable id="table" binding="#{overview.dataTable}"
                                      value="#{dataModel}" var="test"
                                      onRowMouseOver="this.style.backgroundColor='#F1F1F1'"
                                      onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'"
                                      reRender="footer">
                                      <f:facet name="header">
                                          <rich:columnGroup>
                                              <rich:column>
                                                  <h:outputText value="Name" />
                                              </rich:column>
                                              <rich:column>
                                                  <h:outputText value="Value" />
                                              </rich:column>
                                          </rich:columnGroup>
                                      </f:facet>
                      
                                      <rich:column filterBy="#{test.name}" filterEvent="onkeyup">
                                          <h:outputText value="#{test.name}" />
                                      </rich:column>
                                      <rich:column>
                                          <h:outputText value="#{test.values.size()}" />
                                      </rich:column>
                      
                                      <f:facet name="footer">
                                          <rich:columnGroup>
                                              <rich:column>
                                                  <h:outputText value="Total" />
                                              </rich:column>
                                              <rich:column id="footer">
                                                  <h:outputText value="#{overview.total}" />
                                              </rich:column>
                                          </rich:columnGroup>
                                      </f:facet>
                                  </rich:dataTable>
                              </h:form>
                          </ui:define>
                      </ui:composition>
                      
                      </html>
                      

                       

                      OverviewBean.java:

                      @Named("overview")
                      @ViewScoped
                      public class OverviewBean {
                      
                          private HtmlDataTable                    dataTable;
                      
                          public HtmlDataTable getDataTable() {
                              return dataTable;
                          }
                      
                          public void setDataTable(HtmlDataTable dataTable) {
                              this.dataTable = dataTable;
                          }
                      
                          public String getTotal() {
                              DataModel dm = DataModel.class.cast( dataTable.getValue() );
                              return String.valueOf( dm.getDataItems().size() );
                          }
                      }
                      

                       

                      BaseModifiableDataModel.java:
                      public abstract class BaseModifiableDataModel<T> extends ExtendedDataModel
                              implements Modifiable {
                      
                          @PersistenceContext(unitName = "test")
                          private EntityManager    manager;
                      
                          private Class<T>        entityClass;
                      
                          public BaseModifiableDataModel(Class<T> entityClass) {
                              super();
                              this.entityClass = entityClass;
                          }
                      
                          private List<T>                dataItems    = new ArrayList<T>();
                      
                          private T                    dataItem;
                      
                          private SequenceRange        cachedRange;
                      
                          private List<T>                cachedItems    = new ArrayList<T>();
                      
                          private List<FilterField>    filterFields;
                      
                          private List<SortField2>    sortFields;
                      
                          private static boolean areEqualRanges(SequenceRange range1,
                                  SequenceRange range2) {
                              if (range1 == null || range2 == null) {
                                  return range1 == null && range2 == null;
                              }
                              else {
                                  return range1.getFirstRow() == range2.getFirstRow()
                                          && range1.getRows() == range2.getRows();
                              }
                          }
                      
                          private CriteriaBuilder createCriteriaBuilder() {
                              return manager.getCriteriaBuilder();
                          }
                      
                          private void appendFilters(FacesContext ctx,
                                  CriteriaBuilder criteriaBuilder, CriteriaQuery<T> criteriaQuery,
                                  Root<T> instance) {
                              if (filterFields != null) {
                                  List<Predicate> predicates = new ArrayList<Predicate>();
                                  for (FilterField filterField : filterFields) {
                                      String propertyName = getPropertyName( ctx,
                                              filterField.getExpression() );
                      
                                      String filterValue = ((ExtendedFilterField) filterField).getFilterValue();
                                      if (filterValue != null && filterValue.length() != 0) {
                                          Path<String> filter = instance.get( propertyName );
                                          predicates.add( criteriaBuilder.like(
                                                  criteriaBuilder.lower( filter ), "%"
                                                          + filterValue.toLowerCase() + "%" ) );
                                      }
                                  }
                      
                                  Predicate[] predicateArray = new Predicate[predicates.size()];
                      
                                  for (int i = 0; i < predicates.size(); i++) {
                                      predicateArray[i] = predicates.get( i );
                                  }
                      
                                  criteriaQuery.where( predicateArray );
                              }
                          }
                      
                          private void appendSorts(FacesContext ctx, CriteriaBuilder criteriaBuilder,
                                  CriteriaQuery<T> criteriaQuery, Root<T> instance) {
                              if (sortFields != null) {
                                  List<Order> orders = new ArrayList<Order>();
                                  for (SortField2 sortField : sortFields) {
                                      Ordering ordering = sortField.getOrdering();
                      
                                      if (Ordering.ASCENDING.equals( ordering )
                                              || Ordering.DESCENDING.equals( ordering )) {
                                          String propertyName = getPropertyName( ctx,
                                                  sortField.getExpression() );
                      
                                          Path<String> path = instance.get( propertyName );
                                          orders.add( Ordering.ASCENDING.equals( ordering ) ? criteriaBuilder.asc( path )
                                                  : criteriaBuilder.desc( path ) );
                                      }
                                      criteriaQuery.orderBy( orders );
                                  }
                              }
                          }
                      
                          private String getPropertyName(FacesContext ctx, Expression expression) {
                              return ctx.getApplication().evaluateExpressionGet( ctx,
                              expression.getExpressionString(), String.class );
                          }
                      
                          @Override
                          public Object getRowKey() {
                              return dataItem;
                          }
                      
                          @Override
                          public void setRowKey(Object key) {
                              this.dataItem = entityClass.cast( key );
                          }
                      
                          @SuppressWarnings("unchecked")
                          @Override
                          public void walk(FacesContext ctx, DataVisitor visitor, Range range,
                                  Object argument) throws IOException {
                              SequenceRange sequenceRange = (SequenceRange) range;
                      
                              if (this.cachedItems == null
                                      || !areEqualRanges( this.cachedRange, sequenceRange )) {
                      
                                  CriteriaBuilder criteriaBuilder = createCriteriaBuilder();
                                  Metamodel model = manager.getMetamodel();
                                  EntityType<T> entity = model.entity( entityClass );
                                  CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery( entityClass );
                                  Root<T> instance = criteriaQuery.from( entity );
                      
                                  appendFilters( ctx, criteriaBuilder, criteriaQuery, instance );
                                  appendSorts( ctx, criteriaBuilder, criteriaQuery, instance );
                      
                                  Query query = manager.createQuery( criteriaQuery );
                      
                                  if (sequenceRange != null) {
                                      int first = sequenceRange.getFirstRow();
                                      int rows = sequenceRange.getRows();
                      
                                      query.setFirstResult( first );
                                      if (rows > 0) {
                                          query.setMaxResults( rows );
                                      }
                                  }
                      
                                  this.cachedRange = sequenceRange;
                                  this.cachedItems = query.getResultList();
                              }
                      
                              this.dataItems.clear();
                      
                              for (T item : this.cachedItems) {
                                  visitor.process( ctx, item, argument );
                                  this.dataItems.add( item );
                              }
                          }
                      
                          @Override
                          public int getRowCount() {
                              return cachedItems.size();
                          }
                      
                          @Override
                          public Object getRowData() {
                              return this.dataItem;
                          }
                      
                          @Override
                          public int getRowIndex() {
                              return -1;
                          }
                      
                          @Override
                          public Object getWrappedData() {
                              return null;
                          }
                      
                          @Override
                          public boolean isRowAvailable() {
                              return (this.dataItem != null);
                          }
                      
                          @Override
                          public void setRowIndex(int rowIndex) {
                          }
                      
                          @Override
                          public void setWrappedData(Object data) {
                          }
                      
                          @Override
                          public void modify(List<FilterField> filterFields,
                                  List<SortField2> sortFields) {
                              this.filterFields = filterFields;
                              this.sortFields = sortFields;
                      
                              this.cachedItems = null;
                              this.cachedRange = null;
                          }
                      
                          public List<T> getDataItems() {
                              return dataItems;
                          }
                      }
                      

                       

                      DataModel.java:

                      @Named("dataModel")
                      @Dependent
                      public class DataModel extends BasisModifiableDataModel<Test> {
                      
                          public DataModel() {
                              super( Test.class );
                          }
                      }
                      

                       

                      The getDataItems() method is called before walk() is called, so I get an empty List of the items in OverviewBean - at every time. Please tell me, how I can rewrite my example to solve the problem. Either I'm too stupid, or JSF is not suitable for problems like that.

                      • 8. Re: Displaying total sums in the footer of a dataTable
                        ilya_shaikovsky

                        I think you should use just something like

                              return String.valueOf( dm.getRowCount() );

                        And your model should return count of data properly. At livedemo that method implemented like:

                         

                        @Override
                        public int getRowCount() {
                        Criteria criteria = createCriteria();
                        appendFilters(FacesContext.getCurrentInstance(), criteria);
                        criteria.setProjection(Projections.rowCount());
                        //There are cast to Number object, related to http://opensource.atlassian.com/projects/hibernate/browse/HHH-3497.
                        //Such kind of implementation should work undependent on what Hibernate library version used.
                        return ((Number)criteria.list().get(0)).intValue();
                        }
                        
                        • 9. Re: Displaying total sums in the footer of a dataTable
                          kado

                          You're right. I was really too stupid for it. I have changed the DataModel using caching, now it does not care which method is called first. Also it is invoked only one query per filter event. The performance is no worse than the original one now...

                           

                          But another problem had to be dealt with this changes. Thus I used JPA 2 with a JEE 6 Environment, I couldn't use @Named on my DisplayBean, because it allows no other Scope as @Dependent. So I had to define a @ManagedBean with @ViewScoped. I can't explain why is this so. The only disadvantage now is because of mixing different technologies.

                           

                          I would like to thank you for the great support here in the forum. This is main reason why we are using Richfaces in our company.

                           

                          But one feature I'd like to have at Data Tables: Support for an "ajax delay" on filter events. It looks not very nice if someone types to fast.

                           

                           

                          Thanks,

                          Jan

                          • 10. Displaying total rowCount of a dataTable
                            anudeepshetty

                            I have similar kind of issue with my code ,

                            i want to render the datascroller of the datatable only if the row count exceeds 5(number of values in the list are >5). currently i use a backing bean variable and render the scroller if backingvariable > 5 , backingvariable=list.size().

                            Is there any other way i could get the total row count instead of using the backingvariable.

                            ?

                            • 11. Re: Displaying total rowCount of a dataTable
                              danielk

                              rich:dataTable is an exciting tag. One of its capabilites is rowKeyVar. With this setting you can output the total count of rows of your table (also with filtering). Just add two things to your rich:dataTable:

                               

                               

                              {code}<rich:dataTable

                                   id="myTable"

                                   binding="#{myBean.dataTable}"

                                   rowKeyVar="rowCount"

                              ...{code}

                               

                              Than you can use the binding in combination with rowKeyVar to show total amount of rows:

                               

                               

                              {code}<h:outputText value="#{myBean.dataTable.rowCount}"/>{code}

                               

                              For the datascroller problem: the datascroller has a property called "renderIfSinglePage". All you have to do is configure this to false, and give your dataTable a maximum of "rows". Than datascroller will only be rendered if datatable contains more rows than defined.