Wednesday, June 23, 2010

Another parent child relationship pitfall (jdo)

Its a rookie's mistake but it stole an hour of my life, so at least I hope this will save someone else's time. I was making an experiment on AppEngine with big collections and I wrote these very simple parent and child classes to help in my test:
@PersistenceCapable
public class ParentWithBidiOwnedChildren {

 @Persistent(mappedBy="parent")
 private Set<ChildWithBidiOwningParent> children;

 public ParentWithBidiOwnedChildren() {
  children = new HashSet<ChildWithBidiOwningParent>();
 }

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 public Key id;

 public void addNewChild() {
  children.add(new ChildWithBidiOwningParent(this));
 }

 public Set<ChildWithBidiOwningParent> getChildren() {
  return children;
 }
}
And child:
@PersistenceCapable
public class ChildWithBidiOwningParent { 
 
 @PrimaryKey 
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 public Key id;
 
 @Persistent
 private final ParentWithBidiOwnedChildren parent;

 public ChildWithBidiOwningParent(ParentWithBidiOwnedChildren parent) {
  this.parent = parent;  
 }

 public ParentWithBidiOwnedChildren getParent() {
  return parent;
 }
}
In Eclipse IDE when you hit ctrl+1 you can quickly create a member field from a constructor parameter:

Eclipse generates a final field which is recommended and desirable in most cases:

What happens next, when you try to persist a parent, you get an exception:

javax.jdo.JDOUserException: Class "com.codeark.appengine.bigcollections.ParentWithBidiOwnedChildren" has collection field "children" and this has no mapping in the table for the element class "com.codeark.appengine.bigcollections.ChildWithBidiOwningParent" owner field "parent"

Immediately I started looking for an error in my annotations, but the smoking gun was that auto generated final keyword, so here is the correct child code:
@PersistenceCapable
public class ChildWithBidiOwningParent { 
 
 @PrimaryKey 
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 public Key id;
 
 @Persistent
 private ParentWithBidiOwnedChildren parent;

 public ChildWithBidiOwningParent(ParentWithBidiOwnedChildren parent) {
  this.parent = parent;  
 }

 public ParentWithBidiOwnedChildren getParent() {
  return parent;
 }
}
That was a very unpleasent hour for me :-)

No comments:

Post a Comment