1
votes

I'm working on GAE-based applications, which uses JDO to access datastore. I need to implement polymorphic relationship between persisted objects.

There's abstract parent class:

@PersistenceCapable
@Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class Parent {
 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
 String id;

    // ....

And several child classes:

@PersistenceCapable (identityType = IdentityType.APPLICATION)
public class Child extends Parent {

    // ....

Also, there's one more class, which should have reference to one of child classes. According to "Polymorphic Relationships" section of "Entity Relationships in JDO" article, the best way to implement such relationship is to store key of an object, so this class looks in the following way:

@PersistenceCapable (identityType = IdentityType.APPLICATION)
public class OtherClass {

    @Persistent
    private String reference;

    // ....

I retrieve string key of referenced object from instance of OtherClass. Then I would like to obtain referenced object itself: it's an instance of one of Parent subclasses. BUT:

JDOObjectNotFoundException exception is thrown (javax.jdo.JDOObjectNotFoundException: No such object FailedObject:...).

FatalNucleusUserException exception is thrown (org.datanucleus.store.appengine.FatalNucleusUserException: Received a request to find an object of kind Parent but the provided identifier is the String representation of a Key for kind Child)

What is correct way to retrieve instance of one of subclasses referenced in another entity?

UPDATE: I found this thread in GAE google group, but frankly it did not help me a lot.

3
Since currently there's no polymorphism support, I made a workaround - store additional information about type of referenced object.Kel

3 Answers

1
votes

I found the same problem with JDO and App Engine, so I started a project that implements a workaround for this. https://code.google.com/p/datanucleus-appengine-patch/ My first test with the code I have now looks okay, feel free to try it out at give me some feedback.

Actually my workaround may solve your problem 2 ways.

  1. I implemented a getObjectById(class, id) that also looks for kinds that are instances of the provided class.
  2. I implemented a getObjectById(oid) that does some special handling of lookup if oid is of type com.google.appengine.api.datastore.Key, then it will figure out the correct class to return.
  3. I added a new annotation @PolymorphicRelationship that will make is easy to handle to workaround that App Engine describes, with storing the keys. Sample shown below:
 @Persist
 public Collection<Key> myChildKeys;

 @NotPersistent
 @PolymorphicRelationship(keyField ="myChildKeys")
 public Collection<TestChild> myChildren;
1
votes

I'm using this rather cancerous and smelly anti-pattern to get around this limitation of JDO/App Engine.

@JsonIgnore
@Persistent(mappedBy="account")
private List<XProvider> xProviders;

@JsonIgnore
@Persistent(mappedBy="account")
private List<YProvider> yProviders;

// TODO: add extra providers here and in getProviders() below...

And then to get the collection:

public List<XProvider> getXProviders() {
    if (xProviders == null) {
        xProviders = new ArrayList<XProvider>();
    }
    return xProviders;
}
//etc with other getters and setters for each collection.

public List<Provider> getProviders() {

    List<Provider> allProviders = new ArrayList<Provider>();

    // TODO: add extra providers here...
    allProviders.addAll(getXProviders());
    allProviders.addAll(getYProviders());

    return allProviders;
}

It's a bad solution, but any port in a storm...

(Also relates a little to this bug, using interfaces as the collection type http://code.google.com/p/datanucleus-appengine/issues/detail?id=207)

0
votes

App Engine's JDO layer doesn't currently support polymorphism. In fact, I'm not sure if JDO supports it in general or not.