13 Replies Latest reply on Apr 14, 2010 3:18 PM by Thomas Küstermann

    Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem

    heapifyman heapifyman Newbie

      I have a question regarding rich:tree component. I'm just trying to get to know the tree component. I have created a single entity bean that has a parent-child relationship with itself and want to display that structure in a rich:tree.

      I found the recursiveTreeNodesAdaptor example and got it working: tree is correctly displayed and expands and collapses alright.

      Now to my question: I've noticed that apparently on every collapse/expand the whole tree is reloaded, i.e. there are a lot of database accesses. For my little example that works fine but I suppose for more complex entities and a lot more entries in the database that could be a performance problem.

      What I'd like to have is that only the part of the tree that is expanded is reloaded. How would I accomplish that? I've found the ajaxSingle attribute but setting it to true didn't change anything. Any hints would be appreciated.
      Thanks.

      Here my code so far:


      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
          xmlns:s="http://jboss.com/products/seam/taglib"
          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:rich="http://richfaces.org/rich"
          xmlns:a4j="http://richfaces.org/a4j"
          template="layout/template.xhtml">
      
      <ui:define name="body">
      
          <rich:panel>
               <f:facet name="header">TreeTest</f:facet>
               <h:form>
                    <rich:tree id="treeTestTree" 
                         switchType="ajax" 
                         ajaxSingle="true"
                         adviseNodeOpened="#{treeTestList.adviseNodeOpened}">
                         <rich:recursiveTreeNodesAdaptor 
                              id="treeTestRecursiveTreeNodesAdaptor"
                               roots="#{treeTestList.rootNodes}" 
                               var="_treeTest"
                               nodes="#{_treeTest.children}" >
                               <rich:treeNode>
                                    <h:outputText value="#{_treeTest.name}" />
                               </rich:treeNode>
                          </rich:recursiveTreeNodesAdaptor>
                     </rich:tree>
              </h:form>
          </rich:panel>
          
      </ui:define>
      
      </ui:composition>


        • 1. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
          dan liu Newbie

          What is the solution?
          I have the same issues as the datamodel is big(the whole tree has aroud 2000 items).


          Thanks /dan

          • 2. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
            heapifyman heapifyman Newbie

            Haven't found one yet.
            Maybe my treeTestList component does not have the right scope?


            Could you perhaps post your code?


            Regards,
            Philip

            • 3. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
              dan liu Newbie

              you probably want to use @Factory for the datamodel loading, as it will loaded only once for the scope. What scope you use, page or conversation?


              I did not yet start coding as I am still investigate whether the tree will be proper for my 2000 tree nodes size. Even the datamodel is only loaded once will be too much. What I really want is to dynamic loading the expand tree node instead of load the model all at once.


              Can you post your code for the testTestList?

              • 4. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                Marc Schmucki Newbie
                I writed my own getChildren() Function. In this Function I'm calling a Business Function, which passes me all children to a specific node. Because of "_treeTest.children" only children, which will be displayed or which could be displayed with one collapse call, will be diplayed =)


                public class MyNode{
                ...

                public List<MyNode> getChildren() {

                ...
                List<MyNode> list = this.nodeService.getChildren([parentNode]);

                ...
                return list;
                }

                Hope it gives you a hint...
                • 5. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                  heapifyman heapifyman Newbie

                  public class MyNode{
                  ...

                  Just to clarify: your MyNode class, is that an entity or some custom implementation of a TreeNode?


                  My treeTest is an entity with the following properties:


                  private java.util.Set<TreeTest> children = new java.util.TreeSet<TreeTest>();
                  private TreeTest parent;



                  rootNodes is a List of TreeTest entities that have no parent, obviously. This list is populated by a method with @Factory annotation. This works well for the initial display of the tree.
                  I was hoping that with the help of Seam's extended persistence context, getChildren() would automatically retrieve the children from the database. In fact, that's what happens when getChildren() is called on one of the root nodes. But getChildren() called on one of those child nodes results in a LazyInitializationException. I don't really understand why.

                  • 6. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                    Marc Schmucki Newbie

                    MyNode is a simple POJO with members like data, parentData, nodeData, parentData, type, icon, etc.
                    Hm, and which ScopeType has your TreeTest?
                    I'm not working with the @Factory, because I want to know how, when, etc. the the root nodes and children are loaded from database.

                    • 7. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                      heapifyman heapifyman Newbie

                      My TreeTest is also just a simple POJO entity. It is not a Seam component. (see code below). Only my TreeTestList a Seam component (also a POJO) with scope conversation now.


                      Maybe I'm getting something seriously wrong here, but I thought that entities need not be Seam components to profit from Seam's extended persistence context?


                      TreeTest entity


                      @javax.persistence.Entity
                      @javax.persistence.Table(name = "TREE_TEST")
                      public class TreeTest extends CaseComponentImpl implements java.io.Serializable, Comparable<TreeTest>
                      {
                          @jcolibri.annotations.SkipAttribute private static final long serialVersionUID = -2364470753794495242L;
                      
                          private java.lang.String name;
                          private java.lang.Long id;
                          
                          private java.util.Set<TreeTest> children = new java.util.TreeSet<TreeTest>();
                          private TreeTest parent;
                      
                          public TreeTest() {}
                      
                          public TreeTest(java.lang.String name) 
                          {
                              setName(name);
                          }
                      
                          public TreeTest(java.lang.String name, java.util.Set<TreeTest> children, TreeTest parent)
                          {
                              setName(name);
                      
                              setChildren(children);
                              setParent(parent);
                          }
                      
                      
                          @javax.persistence.Column(name = "NAME", nullable = false, insertable = true, updatable = true)
                          @org.hibernate.validator.NotNull
                          public java.lang.String getName()
                          {
                              return name;
                          }
                      
                          public void setName(java.lang.String value)
                          {
                              this.name = value;
                          }
                          
                          @javax.persistence.Id
                          @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
                          @javax.persistence.Column(name = "ID", nullable = false, insertable = true, updatable = true)
                          public java.lang.Long getId()
                          {
                              return id;
                          }
                      
                          public void setId(java.lang.Long value)
                          {
                              this.id = value;
                          }
                      
                          @javax.persistence.OneToMany(mappedBy = "parent")
                          @org.hibernate.annotations.IndexColumn(name = "TREE_TEST_CHILDREN_IDX")
                          public java.util.Set<TreeTest> getChildren()
                          {
                              return this.children;
                          }
                      
                          public void setChildren (java.util.Set<TreeTest> children)
                          {
                              this.children = children;
                          }
                      
                          @javax.persistence.ManyToOne()
                          @javax.persistence.JoinColumn(name = "PARENT_FK")
                          public TreeTest getParent()
                          {
                              return this.parent;
                          }
                      
                          public void setParent(TreeTest parent)
                          {
                              this.parent = parent;
                          }
                      
                          public boolean equals(Object object)
                          {
                              if (this == object)
                              {
                                  return true;
                              }
                              if (!(object instanceof TreeTest))
                              {
                                  return false;
                              }
                              final TreeTest that = (TreeTest)object;
                              if (this.getId() == null || that.getId() == null || !this.getId().equals(that.getId()))
                              {
                                  return false;
                              }
                              return true;
                          }
                      
                          public int hashCode()
                          {
                              int hashCode = 0;
                              hashCode = 29 * hashCode + (getId() == null ? 0 : getId().hashCode());
                      
                              return hashCode;
                          }
                      
                          public String toString()
                          {
                              StringBuilder sb = new StringBuilder();
                              sb.append("TreeTest(=");
                              sb.append("name: ");
                              sb.append(getName());
                              sb.append(", id: ");
                              sb.append(getId());
                              sb.append(")");
                              return sb.toString();
                          }
                      
                          public int compareTo(TreeTest o)
                          {
                              int cmp = 0;
                              if (this.getId() != null)
                              {
                                  cmp = this.getId().compareTo(o.getId());
                              }
                              else
                              {
                                  if (this.getName() != null)
                                  {
                                      cmp = (cmp != 0 ? cmp : this.getName().compareTo(o.getName()));
                                  }
                              }
                              return cmp;
                          }
                      }



                      • 8. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                        heapifyman heapifyman Newbie

                        Somehow, it is now working for me with a simple:



                                 <h:form>              
                                      <rich:tree id="treeTestTree"
                                           switchType="ajax" 
                                           ajaxSingle="true">
                                           <rich:recursiveTreeNodesAdaptor 
                                                id="treeTestRecursiveTreeNodesAdaptor"
                                                 roots="#{treeTestList.rootNodes}" 
                                                 var="_node"
                                                 nodes="#{_node.children}" >
                                                 <rich:treeNode>
                                                      <h:outputText value="#{_node.name}" />
                                                 </rich:treeNode>
                                            </rich:recursiveTreeNodesAdaptor>
                                       </rich:tree>
                                </h:form>



                        where #{treeTestList.rootNodes} returns a List<TreeTest> and #{_node.children} just invokes the TreeTest's getChildern() method.


                        Don't know why but this seems to be solved now.
                        Thanks for all the help.

                        • 9. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                          Amit Newbie

                          I am facing exactly the same problem. Every time I click on node of tree whole table gets loaded.


                          Here is my .xhtml




                              <h:form>    
                                  <rich:tree style="width:300px" switchType="ajax" ajaxSingle="true" >
                                      <rich:recursiveTreeNodesAdaptor roots="#{locationTreeAction.sourceRoots}" var="item" nodes="#{item.children}" />
                                  </rich:tree>
                              </h:form>





                          And here is LocationTreeAction





                          @Stateless
                          @Name("locationTreeAction")
                          @Scope(ScopeType.CONVERSATION)
                          public class LocationTreeAction implements LocationTree {
                               @PersistenceContext
                               EntityManager entityManager;
                          
                          
                               public synchronized List<Location> getSourceRoots(){
                          
                                    List<Location> locationList = entityManager.createQuery("select l from Location l where l.parent is null")
                                              .getResultList();
                          
                                    System.out.println("locationList.size " + locationList.size());
                                    
                                    return locationList;
                          
                               }
                          }




                          Any clue why it loads whole table? Shouldn't is just load the children of expanded node.


                          Any help is highly appreciated.


                          Thanks
                          Amit.


                          • 10. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                            Thomas Küstermann Newbie

                            Amit Bhayani wrote on Apr 10, 2010 18:34:

                            Any clue why it loads whole table? Shouldn't is just load the children of expanded node.


                            The expression

                            #{locationTreeAction.sourceRoots}


                            is evaluated on each request which hits the database. A possible solution is to outject the fetched results into a context variable of a longer lasting scope (such as PAGE, CONVERSATION, SESSION ...). Try this for example:



                            • xhtml



                            <rich:tree>
                                <rich:recursiveTreeNodesAdaptor roots="#{sourceRoots}" var="item" nodes="#{item.children}" />
                            </rich:tree>




                            • Bean



                            @Factory(autoCreate=true, scope=ScopeType.PAGE)
                            public List<Location> getSourceRoots() {
                                List<Location> locationList =
                                    entityManager.createQuery("select l from Location l where l.parent is null")
                                     .getResultList();
                                return locationList;
                            }
                            

                            • 11. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                              Amit Newbie

                              Thanks Thomas,


                              That really helped. Posting my code here for benefit of others


                              in .xhtml



                                             <rich:tree style="width:300px" switchType="ajax" ajaxSingle="true"
                                                  nodeSelectListener="#{locationTreeAction.processSelection}"
                                                  ajaxSubmitSelection="true">
                                                  <rich:recursiveTreeNodesAdaptor roots="#{sourceRoots}" var="item"
                                                       nodes="#{item.children}" />
                                             </rich:tree>



                              The bean





                              @Stateful
                              @Name("locationTreeAction")
                              @Scope(ScopeType.SESSION)
                              public class LocationTreeAction implements LocationTree {
                              
                                   @PersistenceContext(type = PersistenceContextType.EXTENDED)
                                   EntityManager entityManager;
                              
                                   List<Location> locationList = null;
                              
                                   private Location selectedLocation;
                              
                                   @Factory(autoCreate = true, scope = ScopeType.SESSION)
                                   public synchronized List<Location> getSourceRoots() {
                              
                                        if (locationList == null) {
                                             locationList = entityManager.createQuery("select l from Location l where l.parent is null").getResultList();
                                        }
                              
                                        System.out.println("locationList.size " + locationList.size());
                              
                                        return locationList;
                              
                                   }
                              
                                   public void processSelection(final NodeSelectedEvent event) {
                                        final HtmlTree tree = (HtmlTree) event.getComponent();
                                        selectedLocation = (Location) tree.getRowData();
                              
                                        System.out.println("Selected Node is " + selectedLocation.toString());
                              
                                   }
                              
                                   @Remove
                                   public void destroy() {
                                   }




                              The Entity





                              @Entity
                              @Name("location")
                              @Table(name = "LOCATION")
                              public class Location implements Serializable{
                              
                                   @Id
                                   @GeneratedValue
                                   @Column(name = "ID")
                                   long id;
                                   
                                   @Column(name = "CODE", unique = true, nullable = false, length = 4)
                                   int     code;
                                   
                                   @Column(name = "NAME", unique = true, nullable = false, length = 15)
                                   String      name;
                                   
                                   
                                   @ManyToOne( targetEntity = Location.class )
                                   @JoinColumn(name = "PARENT_ID", nullable = true)
                                   Location parent;
                                   
                                   @OneToMany(mappedBy = "parent")
                                   Set<Location> children = new HashSet<Location>();
                                   
                                   
                                   public long getId() {
                                        return id;
                                   }
                              
                                   public void setId(long id) {
                                        this.id = id;
                                   }
                              
                                   public int getCode() {
                                        return code;
                                   }
                              
                                   public void setCode(int code) {
                                        this.code = code;
                                   }
                              
                                   public String getName() {
                                        return name;
                                   }
                              
                                   public void setName(String name) {
                                        this.name = name;
                                   }
                              
                                   public Location getParent() {
                                        return parent;
                                   }
                              
                                   public void setParent(Location parent) {
                                        this.parent = parent;
                                   }
                              
                                   public Set<Location> getChildren() {
                                        return children;
                                   }
                              
                                   public void setChildren(Set<Location> children) {
                                        this.children = children;
                                   }
                              
                                  public String toString() {
                                      return this.name;
                                  }
                              }




                              • 12. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                                Amit Newbie

                                Posted too soon :)


                                Though above solution works, it always retrieves the first level children (Children of child retrieved when child node expands which is correct. ). What I want is only parents should be retrieved when first query select l from Location l where l.parent is null is executed. And once user clicks on any parent node the children should be retrieved.



                                I am looking at Tag Information for Tree My Link but nothing so far.


                                Any help here is highly appreciated.


                                Thanks once again
                                Amit.


                                • 13. Re: Richfaces recursiveTreeNodesAdaptor - dynamic loading Problem
                                  Thomas Küstermann Newbie

                                  If I understand you right the query select l from Location l where l.parent is null should retrieve only the top level nodes (parent is null). If you expand a top level node, the immediate child nodes should be fetched (Location.children). Sorry, I can't see your problem so far! You may supply us with a more detailed description of what you want to achive and what your problem exactly is.