4 Replies Latest reply on Mar 12, 2008 6:17 PM by mars1412

    howto: parent-child selection-performance?

      I have a parent/child relationship between 2 entities: CategoryGroup and Category


      Now I am thinking of a way to let the user select an arbitrary number of categories (and later store the selected categories in a subtable of the user)


      I'm a newbie and thus having some problems here.


      on the gui-side I use rich:tabPanel and show one panel for each CategoryGroup and within the panels I want to show one check-box for each Category:


      <rich:tabPanel switchType="client">
              <c:forEach items="#{categoryGroupList.resultList}" var="categoryGroup">
                      <rich:tab label="#{categoryGroup.name}">
                              <rich:dataGrid value="#{categoryGroup.categories}" var="category" columns="4" elements="12">
                                      <h:selectBooleanCheckbox value="#{registration.selectedCategoryIds[category.id]}" />
                                      #{category.name}
                              </rich:dataGrid>
                      </rich:tab>
              </c:forEach>
      </rich:tabPanel>
      



      1) c:each?
      I can remember some forum posts, that state, that c:forEach should not be used, and that it has bad performance - so I'd like to replace it.
      but since rich:tabPanel needs a rich:tab element in the JSF view, I don't see how I could do that - or what to do instead.


      2)  performance: initial page rendering
      When the page is rendered, categoryGroupList.resultList will select all CategoryGroups. the Category-List on these entites are fetched lazily, which means that I have 1 select plus one select for each CategoryGroup.
      what could I do about this?




      select categoryGroup from CategoryGroup categoryGroup inner join fetch categoryGroup.categories



      3)  performance: ajax call / page reloading


      when I make any ajax call to the server (from other elements on the page), or any validation fails, the categoryGroupList statement will be executed again, but the statement for the Categories will not be called.
      can anyone explain please?




        • 1. Re: howto: parent-child selection-performance?
          keithnaas

          The biggest danger with JSTL tags mixing facelet TagHandlers with JSF Components.  This is a blessing and a curse.  The blessing is that the size of the JSF Component tree is shrunk when using TagHandlers.  The curse is that they are evaluated at tree creation, not during page rendering.  This means that during the course of the rendering of the page, if the value of the value attribute changes, the output will not be what you expect to see.


          Read http://www.ilikespam.com/blog/c:foreach-vs-ui:repeat-in-facelets to see why this is so and how to workaround it.


          Also, richfaces:repeat
          might fit for your needs.

          • 2. Re: howto: parent-child selection-performance?

            tx 4 the answer, but I am afraid JSF components (like ui:repeat, a4j:repeat) will never work instead of c:forEach in this case:



            rich:tabPanel expects to see rich:tabs as children. ui:repeat as a child is not tasted good for it.

            see rich:taba and a4j:repeat don't seem to work together


            so my resume for question 1 is: there is no way around c:forEach


            2) any ideas, how I could prevent the lazy loading in this case?


            3) I don't understand, why the categoryGroupList statement is executed: my thoughts:



            1. categoryGroupList.resultList is used in the c:forEach

            2. c:forEach is no JSF component, but a TagHandler, so it should be executed only once, when the view is build (not at render time) - and since the ajax-call is a postback, the view, that   has been built when the page was first hit, should be reused (and not be built again)




            The short answer is that a new view is built for every request which is not a postback.

            from http://www.ilikespam.com/blog/c:foreach-vs-ui:repeat-in-facelets


            • 3. Re: howto: parent-child selection-performance?

              After further investigations, I found out the following for problem 3)


              the categoryGroupList is accessed in the applyRequestValues phase.


              my wild-guess here is, that this is neccessary, because I access this list indirectly via:


              #{registration.selectedCategoryIds[category.id]}



              which resides in the forEach loop and thus 'category.id' from the code above will be converted to smth. like this:


              categoryGroupList.resultList[x].category.id



              if this is correct, then now it is clear to me why the categoryGroupList is always accessed.
              Solution: I simply moved this Seam-POJO from its default event scope to the conversation scope


              I still need an answer for my problem 2: lazy loading

              • 4. Re: howto: parent-child selection-performance?

                ad question 2)


                I finally decided to cache the categories in an Application scoped seam pojo. here comes my code, if anyone is interessted.


                @Name("categoryCache")
                @Scope(ScopeType.APPLICATION)
                @Synchronized
                public class CategoryCache {
                     
                     @In
                     EntityManager entityManager;
                     
                     @Logger
                     Log log;
                     
                     private Set<CategoryGroup> categoryGroups = new HashSet<CategoryGroup>();
                     
                     @Create
                     @SuppressWarnings("unchecked")
                     @Observer({"org.jboss.seam.afterTransactionSuccess.CategoryGroup",
                                  "org.jboss.seam.afterTransactionSuccess.Category"})
                     public void initList() {
                          log.info("reinitializing CategoryCache");
                          categoryGroups.clear();
                          List<CategoryGroup> cgs = (List<CategoryGroup>)entityManager.createQuery("select cg  from CategoryGroup cg inner join fetch cg.categories").getResultList();
                          Iterator<CategoryGroup> iterator = cgs.iterator();
                          while (iterator.hasNext()) {
                               categoryGroups.add(iterator.next());
                          }
                     }
                     
                     @ReadOnly
                     public Set<CategoryGroup> getCategories() {
                          return categoryGroups;
                     }
                
                }



                some explanations:



                • I am not sure if this component really needs to be @Synchronized. see Application scope and synchronisation

                • the fetch join will give me one row per categoryGroup: that's why I copy the results to a Set (to make the categoryGroups unique)

                • I am not sure, if @ReadOnly really helps to improve performance on method getCategories() - but I hope so :)