3 Replies Latest reply on Oct 17, 2011 2:30 AM by kragoth

    DataTable and ajax performance problem

    danielrowe
      I am using a RichFaces DataTable, and any Ajax-like thing I do (in my case, either setting a transient property on an entity through a checkbox in a data-row, or switching to another page of results using the RichFaces DataScroller), results in ALL of the entities in the backing list being accessed.  With a large list (more than a few thousand), this is a major performance problem.  Actually, all of the entities are accessed twice.

      When I say "accessed", I mean that each entity has all of its getter properties invoked (the setter properties are NOT invoked), even properties that are not displayed in table columns.

      I have tried two different paging strategies -- only giving the current page of data to the DataTable -- but the issue is still there (although in both cases, I still had all the items in the list -- I just served them up a page at a time through a custom data-model).

      There is something I clearly don't understand about how the data-table works, but others must have faced this issue, so I am hopeful that there is an obvious solution.

      Here is a simplified version of my data-table (I removed some of the columns):

                  <rich:dataTable eventsQueue="default" reRender="dataScroller" styleClass="main-table" id="contactList" rows="20" var="contact" value="#{contacts}" rowClasses="tableRowGrey,tableRowWhite" columnsWidth="33px,130px,130px" columnClasses="rc-liteColumn,rc-liteColumn_lj_pad">
                      <rich:column>
                          <h:selectBooleanCheckbox id="deleteCB" required="true" value="#{contact.entitySelected}">
                              <a4j:support event="onchange" eventsQueue="default" requestDelay="1000" reRender="deleteCB" />
                          </h:selectBooleanCheckbox>
                      </rich:column>
                     
                      <rich:column >
                          <f:facet name="header">Last Name</f:facet>
                          <h:outputText>#{contact.lastName}</h:outputText>
                      </rich:column>
                     
                      <rich:column >
                          <f:facet name="header">First Name</f:facet>
                          <h:outputText>#{contact.firstName}</h:outputText>
                      </rich:column>
                  </rich:dataTable>

      In my manager bean (stateless), the list of entities:

         // Bijected list of contacts (the active contact list)
         @In(required = false, scope = ScopeType.CONVERSATION) @Out(required = false, scope = ScopeType.CONVERSATION)
         protected List<Contact> contacts;

      And a factory method (which is only called once):

         @Factory("contacts")
         @Restrict("#{identity.loggedIn}")
         public void getContacts() {
            Long groupID = (activeContactList != null) ? activeContactList.getId() : null;
            contacts = ContactListCommon.getContacts(entityManager, standardAccount.getId(), groupID);
         }
        • 1. Re: DataTable and ajax performance problem
          cosmo

          Have you checked this?(1)(2)

          • 2. Re: DataTable and ajax performance problem
            danielrowe

            Thanks Aldo.  I had seen the first part, but not the second.  Good stuff in there, but it doesn't seem to solve my issue.  I am pretty sure what is happening is that in the JSF render response phase, Seam copies over all the context variables in the running conversation, and I happen to have my entire contact-list sitting in the conversation.  I think my solution will have to involve a paging strategy where I only keep as few pages as possible, so only those which have been visited are copied over (and it is during the render-response phase that all the contacts are being accessed).

            • 3. Re: DataTable and ajax performance problem
              kragoth

              @BypassInterceptors is your friend.


              Part (not all, but probably a good proportion) of your problem is going to be purely from the way JSF works. In your page every EL expression can(will) be accessed (read resolved) more then once during the JSF lifecycle. Because you are using Seam this means that the full Seam stack has to be built and destroyed on every single access. A list of a few thousand items is really not very large. In Java processing time we are talking milliseconds so, the problem is not the list itself but everything that happens around it - the JSF/Seam stack.


              If the method that is accessed via your EL expression is not marked with @BypassInterceptors then the full Seam interceptor stack is going to be applied. This is a very large hit to your performance.


              You may need to restructure how you build and access this list because I'm pretty sure the use of @Factory wont work with @BypassInterceptors but, the change should be relatively simple.


              Basically you'll end up with something like this.


              @Name("MyBean")
              public class MyBean {
                  //bunch of variables relevant to what we are doing
                  private List<Contact> contacts;
              
                  /* You may want to do this at some other point depending on how you query for your data.
                   * But, somewhere somehow you need to initialise your collection. @Create has it's limitations
                   * so you'll need to find what works for you. My app is vastly different then most Seam apps
                   * because I have built my own navigation framework that handles doing just this.
                  */
                  @Create
                  public void init() {
                      buildContactList();
                  }
              
                  private void buildContacdtList() {
                      contacts = query.list("select contacts"); //yeah you get the idea.
                  }
              
                  @BypassInterceptors
                  public List<Contact> getContacts() {
                      return this.contacts;
                  }
              
                  public void setContacts(List<Contact> contacts) {
                      this.contacts = contacts;
                  }
              }
              



              Now your xhtml should use


              <rich:dataTable value="#{MyBean.contacts}" ..... >
              ...
              </rich:dataTable>
              



              And if everything is done right then you should see a massive performance gain.