The explanation for the exception is in the exception text itself.
Each
entity's key contains the ancestor information for that entity:
http://code.google.com/appengine/articles/storage_breakdown.html
<http://code.google.com/appengine/articles/
storage_breakdown.html>"A path is
a concatenation of entity keys. Every path begins with the key of
the root
entity (which may be the current entity itself) in the current
entity group.
If the current entity is not the root, then the key of each
ancestor is
appended to the path, from top to bottom, until the current
entity's key is
appended."
That is, if you persist Venue before persisting its parent, you get
the key
Venue(ID). Were you to persist the ancestors first, you would get an
unencoded key of Person(PERSON_ID)/TIP(TIP_ID)/Venue(VENUE_ID).
You've got a few options here:
1. Store the Key instead. This won't force the entities into an
entity group
relationship. The tradeoff here is that you lose the ability to
perform
transactions on these entities.
2. Restructure your entity groups. Remember that these are in a
tree-like
hierarchy with a root entity and child entities. This limits the
way you can
design one-to-many relationships without using keys, as entities
cannot be
part of multiple entity groups. Our docs on this are
here:http://code.google.com/appengine/docs/java/datastore/relationships.html
On Sun, Jan 31, 2010 at 6:28 AM, Wong <lhw...@gmail.com> wrote:
In my application I have bi-directional one-to-many relationship
between Venue and Tip.
@Entity
public class Venue implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
@OneToMany(cascade = CascadeType.ALL, mappedBy="venue")
private List<Tip> tips;
@Entity
public class Tip implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private Integer version;
@ManyToOne(fetch = FetchType.LAZY)
private Venue venue;
I have venue object persisted and then tip is added to the venue. I
don't any any problem in doing this.
Venue venue = venueService.find(key);
venue.addTip(tip);
venueService.merge(venue);
Then I expand the relationship to have another bi-directional one-
to-
many relationship between Person and Tip. Tip is posted by a Person
and for a Venue. One Person can post more than one Tips for a Venue.
@Entity
public class Tip implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
@ManyToOne(fetch = FetchType.LAZY)
private Venue venue;
@ManyToOne(fetch = FetchType.LAZY)
private Person postedBy;
@Entity
public class Person implements Serializable, UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
@OneToMany(cascade = CascadeType.ALL, mappedBy="postedBy")
private List<Tip> tips;
Venue venue = venueService.find(key);
Person person = personService.find(userId);
venue.addTip(tip);
person.addTip(tip);
venueService.merge(venue);
I am getting the following error:
Detected attempt to establish Person(29)/Tip(125) as the parent of
Venue(2) but the entity identified by Venue(2) has already been
persisted without a parent. A parent cannot be established or
changed
once an object has been persisted.
org.datanucleus.store.appengine.FatalNucleusUserException: Detected
attempt to establish Person(29)/Tip(125) as the parent of Venue(2)
but
the entity identified by Venue(2) has already been persisted
without a
parent. A parent cannot be established or changed once an object has
been persisted. at
org
.datanucleus
.store.appengine.DatastoreRelationFieldManager.checkForParentSwitch
(DatastoreRelationFieldManager.java:204) at
org.datanucleus.store.appengine.DatastoreRelationFieldManager
$1.setObjectViaMapping(DatastoreRelationFieldManager.java:125) at
org.datanucleus.store.appengine.DatastoreRelationFieldManager
$1.apply
(DatastoreRelationFieldManager.java:104) at
org
.datanucleus
.store.appengine.DatastoreRelationFieldManager.storeRelations
(DatastoreRelationFieldManager.java:78) at
org.datanucleus.store.appengine.DatastoreFieldManager.storeRelations
(DatastoreFieldManager.java:812) at
org
.datanucleus
.store.appengine.DatastorePersistenceHandler.insertPostProcess
(DatastorePersistenceHandler.java:288) at
org
.datanucleus
.store.appengine.DatastorePersistenceHandler.insertObjects
(DatastorePersistenceHandler.java:241) at
org
.datanucleus
.store.appengine.DatastorePersistenceHandler.insertObject
(DatastorePersistenceHandler.java:225) at
org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent
(JDOStateManagerImpl.java:3185) at
org.datanucleus.state.JDOStateManagerImpl.flush
(JDOStateManagerImpl.java:4513) at
org.datanucleus.sco.SCOUtils.validateObjectForWriting(SCOUtils.java:
1494) at
org
.datanucleus
.store
.mapped.scostore.ElementContainerStore.validateElementForWriting
(ElementContainerStore.java:380) at
org
.datanucleus
.store.mapped.scostore.FKListStore.validateElementForWriting
(FKListStore.java:609) at
org.datanucleus.store.mapped.scostore.FKListStore.internalAdd
(FKListStore.java:344) at
org.datanucleus.store.mapped.scostore.AbstractListStore.add
(AbstractListStore.java:105) at org.datanucleus.sco.backed.List.add
(List.java:649) at guestbook.domain.Person.addTip(Person.java:164)
at
guestbook.web.mvc.TipController.create(TipController.java:94) at
Any idea? Your help is very much appreciated.
--
You received this message because you are subscribed to the Google
Groups
"Google App Engine for Java" group.
To post to this group, send email to
google-appengine-j...@googlegroups.com.
To unsubscribe from this group, send email to
google-appengine-java+unsubscr...@googlegroups.com<google-appengine-java%2bunsubscr...@googlegroups.com
>
.
For more options, visit this group at
http://groups.google.com/group/google-appengine-java?hl=en.
--
Ikai Lan
Developer Programs Engineer, Google App Enginehttp://googleappengine.blogspot.com
|http://twitter.com/app_engine