Can self-join and single-table inheritance co-exist in Ebean?

552 views
Skip to first unread message

Joseph Conley

unread,
Jan 23, 2012, 1:30:50 PM1/23/12
to eb...@googlegroups.com
I have a use case where I would like to implement both a self-join and single-table inheritance for the same entity.   I have a NODE table with three fields, ID, Name, and parent_node_id (which is null if the node row is a root node).  Here is my example with just the self-join, which works without issue.

public class Node {
@Id
public int id;
public String name;

@ManyToOne
@JoinColumn(name="parent_node_id")
public Node parent;
//TODO need to ensure children and fields are pre-fetched (can't fetch from within bean)
@OneToMany(mappedBy="parent")
public List<Node> children = new ArrayList<DataNode>();
}

Now, I'd like to add a discriminator column and subclass this Node class to achieve single-table inheritance, making my Node class abstract.  However, when I try it with this setup (using NodeA as an implementing class of Node), I run into the following error:

Exception in thread "main" java.lang.RuntimeException: getIntercept children on [node.Node] type[node.NodeA$$EntityBean$exchange] threw error.
at com.avaje.ebeaninternal.server.deploy.BeanProperty.getValueIntercept(BeanProperty.java:912)
at com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany.getValueIntercept(BeanPropertyAssocMany.java:189)
at com.avaje.ebeaninternal.server.deploy.BeanProperty.elGetValue(BeanProperty.java:937)
at com.avaje.ebeaninternal.server.query.CQuery.createNewDetailCollection(CQuery.java:632)
at com.avaje.ebeaninternal.server.query.CQuery.readBeanInternal(CQuery.java:596)
at com.avaje.ebeaninternal.server.query.CQuery.hasNextBean(CQuery.java:710)
at com.avaje.ebeaninternal.server.query.CQuery.readTheRows(CQuery.java:696)
at com.avaje.ebeaninternal.server.query.CQuery.readCollection(CQuery.java:663)
at com.avaje.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:203)
at com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine.findMany(DefaultOrmQueryEngine.java:96)
at com.avaje.ebeaninternal.server.core.OrmQueryRequest.findList(OrmQueryRequest.java:315)
at com.avaje.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1534)
at com.avaje.ebeaninternal.server.core.DefaultBeanLoader.loadMany(DefaultBeanLoader.java:162)
at com.avaje.ebeaninternal.server.core.DefaultServer.loadMany(DefaultServer.java:516)
at com.avaje.ebeaninternal.server.loadcontext.DLoadManyContext.loadMany(DLoadManyContext.java:158)
at com.avaje.ebean.common.AbstractBeanCollection.lazyLoadCollection(AbstractBeanCollection.java:147)
at com.avaje.ebean.common.BeanList.init(BeanList.java:116)
at com.avaje.ebean.common.BeanList.size(BeanList.java:408)
at com.mainstream.exchange.data.Test.main(Test.java:20)
Caused by: java.lang.RuntimeException: Not expecting this method to be called on [node.Node.children] as it is a NON ID property on an abstract class
at com.avaje.ebeaninternal.server.deploy.ReflectGetter$NonIdGetter.get(ReflectGetter.java:96)
at com.avaje.ebeaninternal.server.deploy.ReflectGetter$NonIdGetter.getIntercept(ReflectGetter.java:100)
at com.avaje.ebeaninternal.server.deploy.BeanProperty.getValueIntercept(BeanProperty.java:905)
... 18 more


Furthermore, when I try to setup the same inheritance structure using the Node class as concrete, I get a ClassCastException: node.NodeA cannot be cast to node.Node.

I haven't found much insight in looking at the source code, is my use case an impossibility in Ebean?

edge

unread,
Jan 24, 2012, 2:55:41 AM1/24/12
to Ebean ORM
Hi Joseph,

I have a something working similar to what you want without any issue.
The exception above looks like you base class is abstract? Try
removing it and see if that helps. Otherwise please provide a test
case

Eddie
> com.avaje.ebean.common.AbstractBeanCollection.lazyLoadCollection(AbstractBe anCollection.java:147)
> at com.avaje.ebean.common.BeanList.init(BeanList.java:116)
> at com.avaje.ebean.common.BeanList.size(BeanList.java:408)
> at com.mainstream.exchange.data.Test.main(Test.java:20)
> Caused by: java.lang.RuntimeException: Not expecting this method to be
> called on [node.Node.children] as it is a NON ID property on an abstract
> class
> at
> com.avaje.ebeaninternal.server.deploy.ReflectGetter$NonIdGetter.get(Reflect Getter.java:96)
> at
> com.avaje.ebeaninternal.server.deploy.ReflectGetter$NonIdGetter.getIntercep t(ReflectGetter.java:100)
> at
> com.avaje.ebeaninternal.server.deploy.BeanProperty.getValueIntercept(BeanPr operty.java:905)

Joseph Conley

unread,
Jan 24, 2012, 8:46:14 AM1/24/12
to eb...@googlegroups.com
Removing the abstract modifier did not help.  Here is my test case:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="node_type_id")
public class Node {
@Id
public int id;
public String name;
@ManyToOne
@JoinColumn(name="parent_node_id")
public Node parent;
@OneToMany(mappedBy="parent")
public List<Node> children = new ArrayList<Node>();

//getters/setters
}

@Entity
@DiscriminatorValue("A")
public class NodeA extends Node {
}

@Entity
@DiscriminatorValue("B")
public class NodeB extends Node {
}

Data (stored in an H2 database):

After running the following code, I get the following error when trying to access the children of root (accessing the parent of a child works fine):

Node child = Ebean.find(Node.class, 2);
System.out.println(child);  //works as expected
System.out.println(child.getParent());  //works as expected 

Node root = Ebean.find(Node.class, 1);
System.out.println(root);  //works as expected 
System.out.println(root.getChildren().size());  //throws following error

Exception in thread "main" java.lang.RuntimeException: getIntercept children on [test.Node] type[test.NodeA$$EntityBean$asset] threw error.
at com.avaje.ebeaninternal.server.deploy.BeanProperty.getValueIntercept(BeanProperty.java:912)
at com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany.getValueIntercept(BeanPropertyAssocMany.java:189)
at com.avaje.ebeaninternal.server.deploy.BeanProperty.elGetValue(BeanProperty.java:937)
at com.avaje.ebeaninternal.server.query.CQuery.createNewDetailCollection(CQuery.java:632)
at com.avaje.ebeaninternal.server.query.CQuery.readBeanInternal(CQuery.java:596)
at com.avaje.ebeaninternal.server.query.CQuery.hasNextBean(CQuery.java:710)
at com.avaje.ebeaninternal.server.query.CQuery.readTheRows(CQuery.java:696)
at com.avaje.ebeaninternal.server.query.CQuery.readCollection(CQuery.java:663)
at com.avaje.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:203)
at com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine.findMany(DefaultOrmQueryEngine.java:96)
at com.avaje.ebeaninternal.server.core.OrmQueryRequest.findList(OrmQueryRequest.java:315)
at com.avaje.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1534)
at com.avaje.ebeaninternal.server.core.DefaultBeanLoader.loadMany(DefaultBeanLoader.java:162)
at com.avaje.ebeaninternal.server.core.DefaultServer.loadMany(DefaultServer.java:516)
at com.avaje.ebeaninternal.server.loadcontext.DLoadManyContext.loadMany(DLoadManyContext.java:158)
at com.avaje.ebean.common.AbstractBeanCollection.lazyLoadCollection(AbstractBeanCollection.java:147)
at com.avaje.ebean.common.BeanList.init(BeanList.java:116)
at com.avaje.ebean.common.BeanList.size(BeanList.java:408)
at test.Test.main(Test.java:12)
Caused by: java.lang.ClassCastException: test.NodeA$$EntityBean$asset cannot be cast to test.Node$$EntityBean$asset
at test.Node$$EntityBean$asset._ebean_getFieldIntercept(Node.java:1)
at com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflect$Getter.getIntercept(EnhanceBeanReflect.java:166)
at com.avaje.ebeaninternal.server.deploy.BeanProperty.getValueIntercept(BeanProperty.java:905)
... 18 more

In thinking about this issue I suspected the error has to do with the limitations of subtyping generic collections (as noted here http://docs.oracle.com/javase/tutorial/java/generics/subtyping.html), but I could be wrong.  Thanks for your quick response.

Joe

edge

unread,
Jan 24, 2012, 8:53:03 AM1/24/12
to Ebean ORM
ok one last thing - can you try enhancing instead of subclassing? I
use enhanced classes and sometimes there is a difference

On Jan 24, 2:46 pm, Joseph Conley <josephpcon...@gmail.com> wrote:
> Removing the abstract modifier did not help.  Here is my test case:
>
> @Entity
> @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
> @DiscriminatorColumn(name="node_type_id")
> public class Node {
> @Id
> public int id;
> public String name;
>  @ManyToOne
> @JoinColumn(name="parent_node_id")
> public Node parent;
>  @OneToMany(mappedBy="parent")
> public List<Node> children = new ArrayList<Node>();
>
> //getters/setters
>
> }
>
> @Entity
> @DiscriminatorValue("A")
> public class NodeA extends Node {
>
> }
>
> @Entity
> @DiscriminatorValue("B")
> public class NodeB extends Node {
>
> }
>
> Data (stored in an H2 database):
>
> ID <http://172.29.65.190:8082/query.do?jsessionid=5724b1cae3c4ffc25b75655...>
> NAME  <http://172.29.65.190:8082/query.do?jsessionid=5724b1cae3c4ffc25b75655...>
> PARENT_NODE_ID  <http://172.29.65.190:8082/query.do?jsessionid=5724b1cae3c4ffc25b75655...>
> NODE_TYPE_ID  <http://172.29.65.190:8082/query.do?jsessionid=5724b1cae3c4ffc25b75655...>
> 1Root*null*A2Child1B3Child1A
> After running the following code, I get the following error when trying to
> access the children of root (accessing the parent of a child works fine):
>
> Node child = Ebean.find(Node.class, 2);
> System.out.println(child);  //works as expected
> System.out.println(child.getParent());  //works as expected
>
> Node root = Ebean.find(Node.class, 1);
> System.out.println(root);  //works as expected
> System.out.println(root.getChildren().size());  //throws following error
>
> Exception in thread "main" java.lang.RuntimeException: getIntercept
> children on [test.Node] type[test.NodeA$$EntityBean$asset] threw error.
> at
> com.avaje.ebeaninternal.server.deploy.BeanProperty.getValueIntercept(BeanPr operty.java:912)
> at
> com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany.getValueInterce pt(BeanPropertyAssocMany.java:189)
> at
> com.avaje.ebeaninternal.server.deploy.BeanProperty.elGetValue(BeanProperty. java:937)
> at
> com.avaje.ebeaninternal.server.query.CQuery.createNewDetailCollection(CQuer y.java:632)
> at
> com.avaje.ebeaninternal.server.query.CQuery.readBeanInternal(CQuery.java:59 6)
> at com.avaje.ebeaninternal.server.query.CQuery.hasNextBean(CQuery.java:710)
> at com.avaje.ebeaninternal.server.query.CQuery.readTheRows(CQuery.java:696)
> at
> com.avaje.ebeaninternal.server.query.CQuery.readCollection(CQuery.java:663)
> at
> com.avaje.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.jav a:203)
> at
> com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine.findMany(Default OrmQueryEngine.java:96)
> at
> com.avaje.ebeaninternal.server.core.OrmQueryRequest.findList(OrmQueryReques t.java:315)
> at
> com.avaje.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.ja va:1534)
> at
> com.avaje.ebeaninternal.server.core.DefaultBeanLoader.loadMany(DefaultBeanL oader.java:162)
> at
> com.avaje.ebeaninternal.server.core.DefaultServer.loadMany(DefaultServer.ja va:516)
> at
> com.avaje.ebeaninternal.server.loadcontext.DLoadManyContext.loadMany(DLoadM anyContext.java:158)
> at
> com.avaje.ebean.common.AbstractBeanCollection.lazyLoadCollection(AbstractBe anCollection.java:147)
> at com.avaje.ebean.common.BeanList.init(BeanList.java:116)
> at com.avaje.ebean.common.BeanList.size(BeanList.java:408)
> at test.Test.main(Test.java:12)
> Caused by: java.lang.ClassCastException: test.NodeA$$EntityBean$asset
> cannot be cast to test.Node$$EntityBean$asset
> at test.Node$$EntityBean$asset._ebean_getFieldIntercept(Node.java:1)
> at
> com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflect$Getter.getInterce pt(EnhanceBeanReflect.java:166)
> at
> com.avaje.ebeaninternal.server.deploy.BeanProperty.getValueIntercept(BeanPr operty.java:905)
> ... 18 more
>
> In thinking about this issue I suspected the error has to do with the
> limitations of subtyping generic collections (as noted herehttp://docs.oracle.com/javase/tutorial/java/generics/subtyping.html), but I

Joseph Conley

unread,
Jan 24, 2012, 10:10:33 AM1/24/12
to eb...@googlegroups.com
You're right, using enhancement solved the issue.  Thanks!

edge

unread,
Jan 24, 2012, 11:55:14 AM1/24/12
to Ebean ORM
ok - still it must be a bug as it should work with subclassing
There is a bug with abstract base classes that needs also to be fixed
But glad to hear you got it to work!
Reply all
Reply to author
Forward
0 new messages