-->
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.  [ 14 posts ] 
Author Message
 Post subject: Collections and polymorphism
PostPosted: Thu Apr 14, 2005 1:27 pm 
Newbie

Joined: Wed Sep 17, 2003 4:20 pm
Posts: 3
I'm having a bit of trouble with getting a mapping to work properly. I have three classes TemplateNode, ProductBranch, and IndexLeaf. TemplateNode is abstract. ProductBranch and IndexLeaf are both concrete subclasses of TemplateNode. ProductBranch holds a list of TemplateNodes which can be either ProductBranch or IndexLeaf instances. When I walk the tree and save the nodes Hibernate puts them into the database correctly with the discriminator values set correctly. However, when I pull the root node out of the database and try to walk the tree again I get a ClassCastException. It seems that no matter what the object's discriminator value was set to it always pulls back a TemplateNode not one of the subclasses. Why am I getting back an instance of an abstract class and not the concrete subclass that was saved?

Code:
    <class name="com.activeindexing.advisor.product.template.TemplateNode" table="product_template_node" discriminator-value="0">
        <id name="id" column="id" type="long" unsaved-value="0">
               <generator class="identity"/>
        </id>

        <discriminator column="type" type="integer"/>

        <property name="productName" column="product_name" type="string"/>

        <many-to-one name="parent" column="parent_node_id" class="com.activeindexing.advisor.product.template.TemplateNode"/>
        <many-to-one name="productPath" column="product_path_id" class="com.activeindexing.advisor.product.ProductPath"/>

        <subclass name="com.activeindexing.advisor.product.template.IndexLeaf" proxy="com.activeindexing.advisor.product.template.IndexLeaf" discriminator-value="1">
            <many-to-one name="indexIdentifier" column="index_identifier_id" class="com.activeindexing.advisor.market.index.IndexIdentifier" cascade="all"/>
        </subclass>
        <subclass name="com.activeindexing.advisor.product.template.ProductBranch" proxy="com.activeindexing.advisor.product.template.ProductBranch" discriminator-value="2">
            <property name="distributionType" column="distribution_type" type="string"/>
            <list name="childList" table="product_template_node_child_list" lazy="false">
                <key column="node_id"/>
                <index column="list_position"/>
                <many-to-many column="child_node_id" class="com.activeindexing.advisor.product.template.TemplateNode"/>
            </list>
        </subclass>

    </class>

Code:
package com.activeindexing.advisor.product.template;

import java.io.Serializable;
import com.activeindexing.advisor.product.ProductPath;

public abstract class TemplateNode implements Serializable {

   private TemplateNode parent;
   private ProductPath productPath;
   private String productName;
   private long id;

   protected TemplateNode() {}

   public TemplateNode (String productName, TemplateNode parent) {

      this.productName = productName;
      this.parent = parent;

      if (parent == null) {
         productPath = new ProductPath();
      }
      else {
         productPath = new ProductPath(parent.getProductPath());
      }

      productPath.addPath(productName);
   }

   public String getProductName () {

      return productName;
   }

   public TemplateNode getParent () {

      return parent;
   }

   public ProductPath getProductPath () {

      return productPath;
   }

   public abstract boolean isLeaf ();

   public abstract TemplateNode clone (TemplateNode clonedParent);

   public long getId() {

      return this.id;
   }

   public void setId(long id) {

      this.id = id;
   }

   public void setParent(TemplateNode parent) {

      this.parent = parent;
   }

   public void setProductName(String productName) {

      this.productName = productName;
   }

   public void setProductPath(ProductPath productPath) {

      this.productPath = productPath;
   }

}


Code:
package com.activeindexing.advisor.product.template;

import java.util.LinkedList;
import java.util.List;
import com.activeindexing.advisor.product.template.distribution.DistributionManager;
import com.activeindexing.advisor.product.template.print.DistributionTracker;

public class ProductBranch extends TemplateNode {

   private String distributionType;
   private List<TemplateNode> childList;

   public ProductBranch() {

      super();

      childList = new LinkedList<TemplateNode>();
   }

   public ProductBranch (String productName, TemplateNode parent) {

      super(productName, parent);

      childList = new LinkedList<TemplateNode>();
   }

   public ProductBranch (String productName, TemplateNode parent, String distributionType) {

      super(productName, parent);

      this.distributionType = distributionType;

      childList = new LinkedList<TemplateNode>();
   }

   public ProductBranch (ProductBranch productBranch, TemplateNode clonedParent) {

      super(productBranch.getProductName(), clonedParent);

      TemplateNode[] childNodes;

      distributionType = productBranch.getDistributionType();

      childNodes = productBranch.getChildren();
      for (int count = 0; count < childNodes.length; count++) {
         addChild(childNodes[count].clone(this));
      }

      this.setId( productBranch.getId() );
   }

   public boolean isLeaf () {

      return false;
   }

   public String getDistributionType () {

      return distributionType;
   }

   public double getWeight (DistributionTracker distributionTracker, TemplateNode child) {

      DistributionManager distributionManager;

      distributionManager = distributionTracker.getDistributionManager(this);

      if (getParent() == null) {
         return distributionManager.getWeight(child);
      }
      else {
         return ((ProductBranch)getParent()).getWeight(distributionTracker, this) * distributionManager.getWeight(child);
      }
   }

   public synchronized void addChild (TemplateNode child) {

      childList.add(child);
   }

   public synchronized TemplateNode[] getChildren () {

      TemplateNode[] children;

      children = new TemplateNode[childList.size()];
      childList.toArray(children);

      return children;
   }

   public TemplateNode clone (TemplateNode clonedParent) {

      return new ProductBranch(this, clonedParent);
   }

   public List getChildList() {

      return this.childList;
   }
   public void setChildList(List childList) {

      this.childList = childList;
   }
   public void setDistributionType(String distributionType) {

      this.distributionType = distributionType;
   }
}


Code:
package com.activeindexing.advisor.product.template;

import com.activeindexing.advisor.market.index.IndexIdentifier;
import com.activeindexing.advisor.product.template.print.DistributionTracker;

public class IndexLeaf extends TemplateNode {

   private IndexIdentifier indexIdentifier;

   public IndexLeaf() {

      super();
   }

   public IndexLeaf (String productName, TemplateNode parent, String indexProvider, String indexName, String[] strategicFractionNames) {

      super(productName, parent);

      indexIdentifier = new IndexIdentifier(indexProvider, indexName, strategicFractionNames);
   }

   public IndexLeaf (IndexLeaf indexLeaf, TemplateNode clonedParent) {

      super(indexLeaf.getProductName(), clonedParent);

      indexIdentifier = indexLeaf.getIndexIdentifier();
      this.setId( indexLeaf.getId() );
   }

   public boolean isLeaf () {

      return true;
   }

   public IndexIdentifier getIndexIdentifier () {

      return indexIdentifier;
   }

   public double getWeight (DistributionTracker distributionTracker) {

      if (getParent() == null) {
         return 1.0D;
      }

      return ((ProductBranch)getParent()).getWeight(distributionTracker, this);
   }

   public TemplateNode clone (TemplateNode clonedParent) {

      return new IndexLeaf(this, clonedParent);
   }

   public void setIndexIdentifier(IndexIdentifier indexIdentifier) {

      this.indexIdentifier = indexIdentifier;
   }
}


Code:
package com.activeindexing.advisor.product.template;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

public class TemplateWalker implements Iterator<TemplateNode> {

   public static enum Traversal {

      BRANCHES, LEAVES, BOTH
   };

   private LinkedList<TemplateNode> nodeList;
   private Traversal traversal;

   public TemplateWalker (TemplateNode templateNode, Traversal traversal) {

      this.traversal = traversal;

      nodeList = new LinkedList<TemplateNode>();

      if ((!traversal.equals(Traversal.BRANCHES)) || (!templateNode.isLeaf())) {
         nodeList.add(templateNode);
      }
   }

   public boolean hasNext () {

      return (!nodeList.isEmpty());
   }

   public TemplateNode next () {

      TemplateNode templateNode;
      TemplateNode[] children;

      if (nodeList.isEmpty()) {
         throw new NoSuchElementException();
      }

      do {
         templateNode = nodeList.removeFirst();

         if (!templateNode.isLeaf()) {
            children = ((ProductBranch)templateNode).getChildren();
            for (int count = 0; count < children.length; count++) {
               if ((!traversal.equals(Traversal.BRANCHES)) || (!children[count].isLeaf())) {
                  nodeList.add(children[count]);
               }
            }
         }
      } while (traversal.equals(Traversal.LEAVES) && (!templateNode.isLeaf()));

      return templateNode;
   }

   public void remove () {

      throw new UnsupportedOperationException();
   }

}


Code:
java.lang.ClassCastException: com.activeindexing.advisor.product.template.TemplateNode$$EnhancerByCGLIB$$80dd16ee
     at com.activeindexing.advisor.product.template.TemplateWalker.next(TemplateWalker.java:46)


I am using Hibernate 3.0 and JDK 1.5


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 27, 2005 1:07 pm 
Regular
Regular

Joined: Tue Jun 22, 2004 8:01 pm
Posts: 106
Location: PowderTown, Utah, USA
It's probably because Hibernate is filling your collection with CGLIB enhanced proxies that are derived from the superclass. If you then use class casts or instanceof's in you code, the code will fail.

This is technically correct from an OO perspective, but in practice can be frustrating. The common response from the Hibernate team will be "refactor your design to be more object oriented." The reason is that casting members of a polymorphic collection technically violates OO encapsulation. But, there are times where strict encapsulation isn't desirable, for example when each individual object exposes a different method signature interface.

For those times, there's another solution. Use the Visitor pattern if you need access to the individual objects.

Check out:
http://www.hibernate.org/280.html

As a final note, Hibernate is probably doing you a favor. If you need to typecast collection members in order to access child-object specific methods, you probably have a flaw in your design that could be refactored differently. The logic that is outside your objects should probably be inside your objects instead, or perhaps injected into the object using a different OO pattern such as the Strategy pattern.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 2:16 pm 
Newbie

Joined: Tue May 10, 2005 1:16 pm
Posts: 16
Location: Vancouver, BC, Canada
I have a similar problem.

I want to represent a tree of objects, some of which may implement random intefaces. I want to be able to go to a node easily (session.load()) process a node's children and identify the ones that implement the intefaces and do different operations on them, according to the implemented interface. The only thing the objects in the tree have in common is that they have to be represented in the tree.

So how else can you do it other than hiding your use of instanceof? The system needs to be reflecting in some way? And it seems the visitor pattern doesn't recognise interfaces - which seems very wierd to me.

My only choice seems to be to have a very smelly composition pattern if I am to continue with my refactoring to use hibernate. This is not acceptable in the case I have.

_________________
http://www.JamesAshepherd.com/


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 2:21 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Guys, if you need instanceof to work polymorphically you are not using the system right. This is really basic OO stuff...


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 2:28 pm 
Newbie

Joined: Tue May 10, 2005 1:16 pm
Posts: 16
Location: Vancouver, BC, Canada
I have a working system I have to refactor. It already exists, I can only work from what I have.

I am trying to use hibernate in the refactoring.

Any suggestions on how people implement a tree using hibernate where the objects have to be used as i have described would be very useful.

Thanks,

_________________
http://www.JamesAshepherd.com/


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 2:53 pm 
Regular
Regular

Joined: Tue Jun 22, 2004 8:01 pm
Posts: 106
Location: PowderTown, Utah, USA
I have a similar problem in my current system. One thing you could try is to combine the Visitor pattern with the Composite pattern. The Composite pattern allows you to represent a hierarchy of objects and iterate them. Visitor allows you to essentially ignore encapsulation while iterating a collection.

So, imagine using composite, which lets you traverse your tree in various ways. If you throw in a "visiting" iteration, you can visit only certain kinds of objects in your list.

I've found that this technique lets me completely bypass the need for instanceof and typecasting. It's good OO form and it works great with Hibernate proxies.

As a final possibility, you could forego cached objects and lazy collections in Hibernate, which means you would not run into proxies at all, freeing you to go crazy with typecasts and instanceof calls (shudder). Think of that as your last resort.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 2:56 pm 
Newbie

Joined: Tue May 10, 2005 1:16 pm
Posts: 16
Location: Vancouver, BC, Canada
Here http://forum.hibernate.org/viewtopic.php?t=942038 is someone with the same problem. The suggested solution would mean hibernate would errode the extensibility of our system. I was very pro hibernate, but after a week of using it, it is isn't as transparent as I was promised...

now I have to decide whether to change the design to accomodate hibernate - which is disappointing. Unless I am missing an established solution I haven't found yet.

_________________
http://www.JamesAshepherd.com/


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 3:03 pm 
Newbie

Joined: Tue May 10, 2005 1:16 pm
Posts: 16
Location: Vancouver, BC, Canada
sorry cardsharp, you posted as i was typing.

I have tried using the visitor pattern, but for some reason the visitor.visit polymorphism trick doesn't pick up that the node implements a given interface. This not working, is what made me post. The visitor only seems to see the class hierarchy.

(Worse it only seems to see members of the class hierarchy that have implemented the visitor accept method - maybe this is normal non hibernate behaviour, I not sure what class type 'this' has in a superclass, I thought it should be the subclass type, but looks like its the superclass type. Which has its reasons)

I'm treble checking that this doesn't work at the moment.

_________________
http://www.JamesAshepherd.com/


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 3:10 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Maybe implement a visitor pattern (which should work flawlessly) without instanceof and Hibernate first, then add Hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 3:13 pm 
Regular
Regular

Joined: Tue Jun 22, 2004 8:01 pm
Posts: 106
Location: PowderTown, Utah, USA
Perhaps you could elaborate on how using the Coposite and Visitor patterns would erode extensibility? In my experience those patterns enhance extensibility, not erode it, because new additions do not require changes to working code. If your design rests upon a huge logic nest of "if" statements containing instanceof's and typecasts, you have to change that code every time you add a new object type. Using Composite and Visitor allow extension without changing a logic nest.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 3:23 pm 
Newbie

Joined: Tue May 10, 2005 1:16 pm
Posts: 16
Location: Vancouver, BC, Canada
sorry I wasnt clear.

You are of course correct, using visitor does not errode the extensibility. It mearly implements it in a different way (it hides the use of instanceof IMHO)

In the test case I have visitor is not working. If I can't use visitor then extensibility is eroded. I am looking for a bug at the moment, as visitor should work.

_________________
http://www.JamesAshepherd.com/


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 5:49 pm 
Newbie

Joined: Tue May 10, 2005 1:16 pm
Posts: 16
Location: Vancouver, BC, Canada
I don't seem to have a bug. Only I thought that the visitor pattern (in java) was more usefull.

The visited classes must be concrete (or could be abstract) java classes. They cannot be interface (as they need to implement the accept method (technicallly I guess the problem is really that there would never be a 'this' keyword that would have a class type of an interface))

This begs the question:

if I session.load() a persisted instance, how do I find out if it implements a given interface?

Do I have to store an isXable flag in the database?

_________________
http://www.JamesAshepherd.com/


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 11, 2005 6:26 pm 
Newbie

Joined: Tue May 10, 2005 1:16 pm
Posts: 16
Location: Vancouver, BC, Canada
Is the only option something like:

Class.forName("MyInterface").isAssignableFrom(Hibernate.getClass(persistedInstance))

yuk

_________________
http://www.JamesAshepherd.com/


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 6:25 pm 
Newbie

Joined: Fri Jul 27, 2007 5:48 pm
Posts: 4
Consider example:

==========================================

public class CollectionInheritance {

public interface Element {
public String getSimpleName();
}

public static class ElementA implements Element{
public String getSimpleName() {return "element A";}
}

public static class ElementB extends ElementA {
public String getComplexName() {return "element B";}
}


public static class HolderA {

private Set<? extends Element> elements = new HashSet<ElementA>();

public Set<? extends Element> getElements() { return elements;}
public void setElements(Set<? extends Element> elements) { this.elements = elements; }

}

public static class HolderB extends HolderA {

private Set<ElementB> elements = new HashSet<ElementB>();

public Set<ElementB> getElements() {return elements;}

}

public static void main(String [] args) {

HolderA holderA = new HolderA();

Set<ElementA> elsa = new HashSet<ElementA>();
elsa.add(new ElementA());

holderA.setElements(elsa);

for(Element element : holderA.getElements()) {
System.out.println(element.getSimpleName());
}

HolderB holderB = new HolderB();

Set<ElementB> elsb = new HashSet<ElementB>();
elsb.add(new ElementB());

holderB.setElements(elsb);

holderB.getElements().add(new ElementB());

for(ElementB element : holderB.getElements()) {
System.out.println(element.getSimpleName());
System.out.println(element.getComplexName());
}

}

}

Output:
--------------------------
element A
element A
element B
==========================================

This is fully functional OO code, that runs perfectly without instanceOf and class casting in plain java, but in hibernate it crashes with ClassCastException on line: for(ElementB element : holderB.getElements()) {

because hibernate places proxies for Element interface instead of ElementB !

How could I tell hibernate to generate proxies of actual Class (ElementB) instead of super class (Element) ?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 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.