views:

873

answers:

1

I'm using the Google App Engine in combination with the Google Web Toolkit to write a bug tracker (to see what the technologies are capable of).

Modelled after Google Code's issue tracker, I decided that an issue can have 0 or more labels, that can be defined beforehand (let's say in the settings).

The label class (CustomLabel):

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class CustomLabel implements Serializable {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
    private Long keyId;

    /**
     * label caption.
     */
    @Persistent
    private String caption;

    // Unimportant getters / setters
}

Now the parent class (Issue):

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Issue implements Serializable {
    private static final long serialVersionUID = 1L;

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    // Replacing the Long key by this key doesn't make a difference
    // @PrimaryKey
    // @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    // @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    // private String encodedKey;

    @Persistent
    private String summary;

    @Persistent
    private String description;

    @Persistent 
    private ArrayList<CustomLabel> labels;

    // Other details
}

When I'm trying to persist a new Issue with existing CustomLabels I get the following exception:

org.datanucleus.exceptions.NucleusUserException: Detected attempt to establish Issue(11) as the parent of CustomLabel(1) but the entity identified by CustomLabel(1) has already been persisted without a parent.  A parent cannot be established or changed once an object has been persisted.

How can this be solved? I cannot use Key's and create an unowned relationship, since I'm sending the objects to the GWT front-end (which is compiled to Javascript and com.google.appengine.api.datastore.Key isn't supported). Besides that would break referential integrity, which is undesirable.

+1  A: 

You can't assign an already existing CustomLabel as a child element to a new Issue. Each CustomLabel entity can only belong to one Issue object, due to the way datanucleus handles relationships. It puts both the parent and child object into the same Entity Group. An entity can only belong to one Entity Group. So let's say you create a Custom Label called "nastybug" and persist it. It now belongs to some entity group X. When you create a new Issue, and go to persist that, it will belong to some Entity Group Y. Datanucleus (and the actual google datastore) will not let you try to store an entity from group X into group Y.

If you want labels to be shared among issues, you will need to use an unowned relationship. You are correct that you cannot pass a Key through GWT's RPC mechanism, so you may need to translate your JDO objects into some other form before sending them.

You can read about entity groups here

Peter Recore
Thank you, that explains a lot, eventhough it's a bit odd, having a relational db background :)
Mythica