-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: OneToMany problem: EAGER vs. LAZY fetching!
PostPosted: Sun Feb 19, 2006 7:11 am 
Newbie

Joined: Fri Jul 29, 2005 4:43 am
Posts: 7
Please help an EJB3-Starter with the following problem.
I have started experimenting a little with the relationships of EJB3 and created a CatalogCategory class, which allows for tree-browsing of a catalog. One category can have several sub-categories and so forth. When using fetchtype EAGER all works fine, but unfortunately the complete catalog category tree is loaded the first time I access the root element. fetch.type LAZY seems to solve this problem, as only those categories will be loaded that i really want to browse through.

I am now having problems implementing this LAZY fetchtype, because I get the following error:
Quote:
failed to lazily initialize a collection of role: dj.ecatalog.entity.CatalogCategoryBean.children, no session or session was closed


Here is my entity class:
Code:
public @Entity @Table(name="catalogcategory") class CatalogCategoryBean implements CatalogCategory {

   /**
    *
    */
   private static final long serialVersionUID = 1L;

   private int id; //UUID

   private DomainBean domain; //DOMAINID

   private Date lastmodified = null; //LASTMODIFIED

   private String name = null; //NAME

   private CatalogCategoryBean parentCategory = null; //PARENTCATEGORYID

   private String properties = null; //PROPERTIES

   private List<ProductTypeBean> productTypes = null;

   private List<CatalogCategoryBean> children = null;

   /**
    * Default constructor
    */
   public CatalogCategoryBean() {
      super();
      System.out.println("category erstellt");
   }

   /**
    * Copy constructor
    * @param original Original in copy origin
    */
   public CatalogCategoryBean(CatalogCategoryBean original) {
      this();
      setDomain(original.getDomain());
      setLastModified(original.getLastModified());
      setName(original.getName());
      setParentCategory(original.getParentCategory());
      setProperties(original.getProperties());
   }

   /**
    * @return the unique id
    */
   public @Id(generate=GeneratorType.AUTO) int getId() {
      return id;
   }

   /**
    * @param id the id to set
    */
   public void setId(int id) {
      this.id = id;
   }

   /**
    * @return the DOMAINID
    */
   public @ManyToOne(optional=false) DomainBean getDomain() {
      return domain;
   }

   /**
    * @param domainid the DOMAINID to set
    */
   public void setDomain(DomainBean domain) {
      this.domain = domain;
   }

   /**
    * @return the LASTMODIFIED
    */
   public Date getLastModified() {
      return lastmodified;
   }

   /**
    * @param lastmodified the LASTMODIFIED to set
    */
   public void setLastModified(Date lastmodified) {
      this.lastmodified = lastmodified;
   }

   /**
    * @return the NAME
    */
   public @Column(length=25) String getName() {
      return name;
   }

   /**
    * @param name the NAME to set
    */
   public void setName(String name) {
      this.name = name;
   }

   /**
    * @return the PARENTCATEGORYID
    */
   public @ManyToOne(optional=true, fetch = FetchType.LAZY) CatalogCategoryBean getParentCategory() {
      return parentCategory;
   }

   /**
    * @param parentcategoryid the PARENTCATEGORYID to set
    */
   public void setParentCategory(CatalogCategoryBean parentcategory) {
      this.parentCategory = parentcategory;
   }

   public @Transient PropertiesDocument getProperties() {
      try {
         return PropertiesDocument.Factory.parse(getPropertiesDocument());
      } catch (Exception e) {
         System.out.println("ERROR:" + e);
      }
      return null;
   }
   
   private @Column(columnDefinition="TEXT") String getPropertiesDocument() {
      return properties;
   }

   private void setPropertiesDocument(String properties) {
      this.properties = properties;
   }

   /**
    * @param properties the PROPERTIES to set
    */
   public void setProperties(PropertiesDocument properties) {
      setPropertiesDocument(properties.toString());
   }
   
   /**
    * Clone method
    * @return The deep copy of this instance
    */
   protected Object clone() {
      return new CatalogCategoryBean(this);
   }

   /**
    * equals method<BR>
    * When the value of all items is the same, true is returned.
    * @return True when value of all items is the same
    */
   public boolean equals(Object obj) {
      if (!(obj instanceof CatalogCategoryBean))
         return false;
      CatalogCategoryBean target = (CatalogCategoryBean) obj;
      if (getId() != target.getId())
         return false;
      if (!getDomain().equals(target.getDomain()))
         return false;
      if (!getLastModified().equals(target.getLastModified()))
         return false;
      if (!getName().equals(target.getName()))
         return false;
      if (!getParentCategory().equals(target.getParentCategory()))
         return false;
      if (!getProperties().equals(target.getProperties()))
         return false;
      return true;
   }

   /**
    * toString method<BR>
    * The value of all items of this instance is written.
    */
   public String toString() {
      StringBuffer buf = new StringBuffer();
      buf.append("getId=" + getId() + ":");
      buf.append("getDomain=" + getDomain() + ":");
      buf.append("getLastModified=" + getLastModified() + ":");
      buf.append("getName=" + getName() + ":");
      buf.append("getParentCategory=" + getParentCategory() + ":");
      buf.append("getProperties=" + getProperties() + ":");
      return buf.toString();
   }

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy="catalogCategory")
   public List<ProductTypeBean> getProductTypes() {
      return productTypes;
   }

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy="parentCategory")
   public List<CatalogCategoryBean> getChildren() {
      
         //hSessionrefresh(myClass);
      return children;
   }

   public void setProductTypes(List<ProductTypeBean> productTypes) {
      this.productTypes = productTypes;
   }

   public void setChildren(List<CatalogCategoryBean> children) {
      this.children = children;
   }

   public void addProductType(ProductTypeBean productType) {
      if (productTypes == null) {
         productTypes = new ArrayList<ProductTypeBean>();
      }
      productType.setCatalogCategory(this);
      productTypes.add(productType);
   }
      
}



If I understand correctly, my bean instance, where I call getChildren from, is detached from the EntityManager's session. So how do I reattach it or keep the session from closing after I retrieve the objects?

Maybe it's worth mentioning, that I am using the
Code:
<tree2>
tag from MyFaces to display the catalog category tree and I've created a TreeModel based on my catalogcategory bean. NOTE! This problem is not a Myfaces issue, as the problem occurs with any other display technology that I have tried. I just paste the code here, so that you get an idea of what I'm trying to accomplish.
The code is as follows:

Code:
public class CatalogCategoryModel implements TreeModel {

   /**
    * Automatically generated serial version ID
    */
   private static final long serialVersionUID = 5380255282827745941L;
   
    private TreeNode root;
    private TreeNode currentNode;
    private TreeState treeState = new TreeStateBase();
   
   public CatalogCategoryModel(List<CatalogCategory> rootCategories)
    {
      root = new TreeNodeBase("catalog", "Kataloge", false);
      System.out.println("Hole");
      Iterator<CatalogCategory> iterator = rootCategories.iterator();
      System.out.println("geholt");
       while (iterator.hasNext()) {
          CatalogCategory category = iterator.next();
          root.getChildren().add(new CatalogCategoryNode(category));
       }
    }

    // see interface   
    public TreeState getTreeState()
    {       
       return treeState;
    }

    // see interface   
    public void setTreeState(TreeState treeState)
    {
        this.treeState = treeState;
    }

    /**
     * Gets the current {@link TreeNode} or <code>null</code> if no node ID is selected.
     * @return The current node
     */
    public TreeNode getNode()
    {
        return currentNode;
    }

    /**
     * Sets the current {@link TreeNode} to the specified node ID, which is a colon-separated list
     * of node indexes.  For instance, "0:0:1" means "the second child node of the first child node
     * under the root node."
     *
     * @param nodeId The id of the node to set
     */
    public void setNodeId(String nodeId)
    {
        if (nodeId == null)
        {
            currentNode = null;
            return;
        }

        currentNode = getNodeById(nodeId);
    }

    /**
     * Gets an array of String containing the ID's of all of the {@link TreeNode}s in the path to
     * the specified node.  The path information will be an array of <code>String</code> objects
     * representing node ID's. The array will starting with the ID of the root node and end with
     * the ID of the specified node.
     *
     * @param nodeId The id of the node for whom the path information is needed.
     * @return String[]
     */
    public String[] getPathInformation(String nodeId)
    {
        if (nodeId == null)
        {
            throw new IllegalArgumentException("Cannot determine parents for a null node.");
        }

        ArrayList pathList = new ArrayList();
        pathList.add(nodeId);

        while (nodeId.lastIndexOf(SEPARATOR) != -1)
        {
            nodeId = nodeId.substring(0, nodeId.lastIndexOf(SEPARATOR));
            pathList.add(nodeId);
        }

        String[] pathInfo = new String[pathList.size()];

        for (int i=0; i < pathInfo.length; i++)
        {
            pathInfo[i] = (String)pathList.get(pathInfo.length - i - 1);
        }

        return pathInfo;
    }

    /**
     * Indicates whether or not the specified {@link TreeNode} is the last child in the <code>List</code>
     * of children.  If the node id provided corresponds to the root node, this returns <code>true</code>.
     *
     * @param nodeId The ID of the node to check
     * @return boolean
     */
    public boolean isLastChild(String nodeId)
    {
        if (nodeId.lastIndexOf(SEPARATOR) == -1)
        {
            // root node considered to be the last child
            return true;
        }

        //first get the id of the parent
        String parentId = nodeId.substring(0, nodeId.lastIndexOf(SEPARATOR));
        String childString = nodeId.substring(nodeId.lastIndexOf(SEPARATOR) + 1);
        int childId = Integer.parseInt(childString);
        TreeNode parentNode = getNodeById(parentId);

        return  childId + 1== parentNode.getChildCount();
    }

    private TreeNode getNodeById(String nodeId)
    {
        TreeNode node = root;
        StringBuffer sb = new StringBuffer();
        StringTokenizer st = new StringTokenizer(nodeId, SEPARATOR);
        sb.append(st.nextToken()).append(SEPARATOR);

        while (st.hasMoreTokens())
        {
            int nodeIndex = Integer.parseInt(st.nextToken());
            sb.append(nodeIndex);

            // don't worry about invalid index, that exception will be caught later and dealt with
            node = (TreeNode)node.getChildren().get(nodeIndex);
            sb.append(SEPARATOR);
        }

        return node;
    }
   
   
}


A catalogCategory is represented as a node and the method getChildren is where the error occurs:
Code:
public class CatalogCategoryNode implements TreeNode, Comparable {

    private static final long serialVersionUID = 278589014441538822L;
   
    protected CatalogCategory category = null;

    private ArrayList children = null;
    private String identifier;

    public CatalogCategoryNode(CatalogCategory category)
    {
       this.category = category;
    }
   
    public boolean isLeaf()
    {
        return getChildCount() == 0;
    }

    public void setLeaf(boolean leaf)
    {
        return;
    }

    public List getChildren()
    {
       if (null == children) {
          children = new ArrayList();
         Iterator<CatalogCategoryBean> categoryIterator = category.getChildren().iterator();
          while (categoryIterator.hasNext()) {
             CatalogCategory category = categoryIterator.next();
             children.add(new CatalogCategoryNode(category));
          }
          /*
          Iterator<ProductTypeBean> productTypeIterator = category.getProductTypes().iterator();
          while (productTypeIterator.hasNext()) {
             ProductTypeBean productType = productTypeIterator.next();
             children.add(new ProductTypeNode(productType));
          }
          */
       }
        return children;
    }

    public String getType()
    {
        return (getChildCount() == 0) ? "catalog" : "category";
    }

    public void setType(String type)
    {
        return;
    }

    public void setDescription(String description)
    {
       category.setName(description);
    }

    public String getDescription()
    {
        return category.getName();
    }

    public void setIdentifier(String identifier)
    {
        this.identifier = identifier;
    }

    public String getIdentifier()
    {
        return identifier;
    }

    public int getChildCount()
    {
        return getChildren().size();
    }

    public int compareTo(Object obj)
    {
        // branches come before leaves, after this criteria nodes are sorted alphabetically
        TreeNode otherNode = (TreeNode)obj;

        if (isLeaf() && !otherNode.isLeaf())
        {
            // leaves come after branches
            return 1;
        }
        else if (!isLeaf() && otherNode.isLeaf())
        {
            // branches come before leaves
            return -1;
        }
        else
        {
            // both nodes are leaves or both node are branches, so compare the descriptions
            return getDescription().compareTo(otherNode.getDescription());
        }
    }

}


The page code looks like this:

Code:
   <t:tree2 id="serverTree" value="#{CatalogHandler.treeData}" var="node"
      varNodeToggler="t" clientSideToggle="false"
      binding="#{CatalogHandler.tree}">
      <f:facet name="catalog">
         <h:panelGroup>
            <t:graphicImage value="/images/yellow-folder-open.png"
               rendered="#{t.nodeExpanded}" border="0" />
            <t:graphicImage value="/images/yellow-folder-closed.png"
               rendered="#{!t.nodeExpanded}" border="0" />
            <h:outputText value="#{node.description}" styleClass="nodeFolder" />
            <h:outputText value=" (#{node.childCount})" styleClass="childCount"
               rendered="#{!empty node.children}" />
         </h:panelGroup>
      </f:facet>
      <f:facet name="category">
         <h:panelGroup>
            <t:graphicImage value="/images/blue-folder-open.gif"
               rendered="#{t.nodeExpanded}" border="0" />
            <t:graphicImage value="/images/blue-folder-closed.png"
               rendered="#{!t.nodeExpanded}" border="0" />
            <h:outputText value="#{node.description}" styleClass="nodeFolder" />
            <h:outputText value=" (#{node.childCount})" styleClass="childCount"
               rendered="#{!empty node.children}" />
         </h:panelGroup>
      </f:facet>
      <f:facet name="producttype">

         <h:dataTable rowClasses="standardTable_Row1,standardTable_Row2"
            headerClass="standardTable_SortHeader"
            footerClass="standardTable_Footer" value="#{node.children}"
            var="producttype">
            <h:column>
               <f:facet name="header">
                  <h:outputText styleClass="vertikal" value="Auswahl" />
               </f:facet>
               <h:selectBooleanCheckbox />
            </h:column>
            <h:column>
               <f:facet name="header">
                  <h:outputText value="Kategorie" />
               </f:facet>
               <h:outputText value="#{node.description}" />
            </h:column>
            <f:facet name="footer">
               <h:outputText value="." />
            </f:facet>
         </h:dataTable>
         
      </f:facet>
   </t:tree2>


Please, anyone, help me get started with LAZY fetching or at least point me to the right docs if this has been dealt with already...


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 4:45 pm 
Beginner
Beginner

Joined: Wed Feb 08, 2006 5:45 pm
Posts: 23
Location: Phoenix, AZ
More information is needed. A copy of the code between opening the Session/EntityManager and closing it and the SQL Hibrnate generates when you retrieve the class would help.

Also, you cannot reference a Lazily fetched Object after your close your Session/EntityManager. If you want to be able to then you must keep the Session/EM that created it open (or disconnect it) because the Object is 'linked' to the Session it came from. If that cannot be done then there might be ways to Eagerly fetch just what you need from the Collection but I would need the code requested at the end of the previous paragraph to determine that.

Speaking of which, is the problem that you are receiving EVERY record in the database or that you are retrieving the correct instances, you just don't want all of them loaded in your collection at first?

-B

_________________
Please rate me if you found my post useful.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 24, 2006 3:56 pm 
Newbie

Joined: Fri Jul 29, 2005 4:43 am
Posts: 7
First of all, thank you for offering to help.
I use the following code to find the root categories:
Code:

public @Stateful @Remote(CatalogCategoryManager.class) class CatalogCategoryManagerBean implements CatalogCategoryManager {

   /**
    * The EntityManager for all eSolution Entities
    */
   private @PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager manager;   
   
   public List<CatalogCategory> getRootCategories() {
      return manager.createQuery("from CatalogCategoryBean categoryBean WHERE categoryBean.parentCategory = null").getResultList();
   }
   
      
   public void attach(CatalogCategory category) {
      manager.merge(category);
   }

}

Furthermore, in another project (web-project) I use the following code to retrieve the catalog manager and create the tree model:
Code:
public class CatalogHandler implements Serializable  {
   
   private CatalogCategoryManager catalogCategoryManager = null;
   private TreeModel treeModel;
   
   public CatalogHandler() {
      try {
         InitialContext context = new InitialContext();
         catalogCategoryManager = (CatalogCategoryManager)context.lookup(CatalogCategoryManager.class.getName());
         treeModel = new CatalogCategoryModel(catalogCategoryManager.getRootCategories());
      } catch (NamingException ne) {
         ne.printStackTrace();
      }
   }


Quote:
Speaking of which, is the problem that you are receiving EVERY record in the database or that you are retrieving the correct instances, you just don't want all of them loaded in your collection at first?

I do get the correct records as results, but I do not want to retrieve all their children at once. I only want to retrieve the children of the node that I expand. I've tried a merge
Quote:
see: public void attach(CatalogCategory category)

on the EntityManager using the CatalogCategoryBean that I want to expand as the parameter, but still the same error.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 14, 2006 3:27 pm 
Newbie

Joined: Fri Jul 29, 2005 4:43 am
Posts: 7
BLudkiewicz wrote:
More information is needed. A copy of the code between opening the Session/EntityManager and closing it and the SQL Hibrnate generates when you retrieve the class would help.

Also, you cannot reference a Lazily fetched Object after your close your Session/EntityManager. If you want to be able to then you must keep the Session/EM that created it open (or disconnect it) because the Object is 'linked' to the Session it came from. If that cannot be done then there might be ways to Eagerly fetch just what you need from the Collection but I would need the code requested at the end of the previous paragraph to determine that.

Speaking of which, is the problem that you are receiving EVERY record in the database or that you are retrieving the correct instances, you just don't want all of them loaded in your collection at first?

-B


Can anyone help?
Is any more information needed?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.