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

    Displaying total rowCount of a dataTable

    Chris Mathrusse Newbie

      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 Master

          How about just to retrieve it from model?

          • 2. Re: Displaying total rowCount of a dataTable
            Chris Mathrusse Newbie

            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.

            • 4. Displaying total sums in the footer of a dataTable
              Jan Glockner Newbie

              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
                Jan Glockner Newbie

                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 Master

                  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
                    Jan Glockner Newbie

                    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 Master

                      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
                        Jan Glockner Newbie

                        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
                          anudeep shetty Newbie

                          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
                            Daniel Ku Newbie

                            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.