The only supported inheritance mapping strategy for polymorphic
relationships is currently for
JDO) new-table for the base class and superclass-table for all
subclasses
JPA) SingleTable.

This means, everything in the inheritance hierarchy ends up in one
entity kind, as you have noticed.  Since we have an owned
relationship, the entities (Car) get an ancestor key of the entity
owning the relationship (Person). And the owning entity gets a
property (cars) with all the keys of the entities in the list. And to
get the java classes for the stored entities, when reading the data
back, we have the discriminator.

Example:
One Person
<Entity [Person(1)]: (kind is “Person”)
        cars = [Person(1)/Car(2), Person(1)/Car(3)]
        name = Former F1 Champion
>

with two Cars
<Entity [Person(1)/Car(2)]:  (ancestor key Person(1), kind is “Car”)
        modelName = Ferrari
        DISCRIMINATOR = org.datanucleus.test.Model
        cars_INTEGER_IDX = 0
>

<Entity [Person(1)/Car(3)]: (ancestor key Person(1), kind is “Car”)
        anotherModelName = Mercedes
        DISCRIMINATOR = org.datanucleus.test.AnotherModel
        cars_INTEGER_IDX = 1
>

Let's assume we would allow splitting the data of the elements of a
relationship over more than one entity kind. To query the data store,
with the low level Datastore API, to get the data for the cars owned
by a person, you have basically the following options to formulate the
queries (http://code.google.com/intl/de-DE/appengine/docs/java/
datastore/queries.html):
1) with a kind (e.g. Car, Model, AnotherModel, ...) and an ancestor
key (key of the person owning the cars)
        => reading all Cars for a person means you have to execute a query
for every entity kind involved in the inheritance hierarchy (would be
five in your case) and then merge the results
2) no kind and no ancestor, but a filter on the entity keys in the
list property (person.cars)
        => this would require a query with an IN filter, but the IN-queries
are translated into multiple Equal-queries (http://gae-java-
persistence.blogspot.com/2009/12/queries-with-and-in-filters.html),
this in turn leads to a query for every entity in the list.
3) no kind but an ancestor key
        => this could also return entities not belonging to the relationship
cars but to an another relationship of person(e.g. addresses). You
would read too many entities and you would have to filter out all
entities not belonging to this relationship.
2) no kind and no ancestor
        => this would require only one query, but the query would return all
entities from your data store and you have to filter them in memory, I
guess this is totally out of the question.

The probably most performant solution here is to forbid splitting up
the data of a collection element over multiple entity kinds and
execute one query with a kind and an ancestor. But there is catch. You
cannot have two relationships with the same element class (at least
not at the moment). This problem could be solved easily, if the
entities for the collection elements would get another property which
says to which relationship it belongs. But in my opinion, it would be
even better, if the low level Datastore API would support (without the
need to split the query) kindless queries with an ancestor key and an
IN filter on “subkeys” of the ancestor key. This should be performant
since, as I understand it, all the data of one entity group resides in
one bigtable row.

I hope this explanation makes it easier for you to see all your
different cars stored in one entity kind. And if you want to save a
little space, you can take advantage of a discriminator map, and
annotate your classes the following way:

@PersistenceCapable()
@Discriminator(column = "D", strategy =
DiscriminatorStrategy.VALUE_MAP)
public abstract class Car { … }

@PersistenceCapable
@Discriminator(value = "M")
public class Model extends Car { …}

Rolf


On 8 Aug., 20:43, mscwd01 <mscw...@gmail.com> wrote:
> This worked nicely!
>
> One question I do have is, I noticed App Engine does not store "Model"
> kinds it instead stores "Car" kinds with a String DISCRIMINATOR
> property that stores what type of Class it is. Is it not possible to
> store "Model" entities? I actually have 5 Classes which extend Car and
> would have preferred to store an entity for each rather than have a
> Car kind comprising of each different class.
>
> Thanks for the help so far though!
>
> On Aug 8, 10:44 am, Rolf Aden <mail.rolf.a...@googlemail.com> wrote:
>
>
>
>
>
>
>
> > It seems you are not using the latest sdk version (be something pre
> > 1.5.1?).
> > As of release 1.5.1 polymorphic relationships are supported for the
> > default inheritance strategy in JDO (new-table for the base class, and
> > superclass-table for all subclasses) and JPA SingleTable. You may want
> > to change Car to
>
> > @PersistenceCapable
> > @Discriminator(column = "DISCRIMINATOR")
> > public abstract class Car {
> > ....
>
> > }
>
> > and keep Model
>
> > @PersistenceCapable
> > public class Model extends Car {
> > ...
>
> > }
>
> > Rolf
>
> > On 8 Aug., 01:35, mscwd01 <mscw...@gmail.com> wrote:
>
> > > Hey,
>
> > > I have 3 classes: Person, Car and Model.
>
> > > Person has an ArrayList property defined:
> > > List<Car> cars;
>
> > > Car is an abstract class which class Model extends, I.e.
>
> > > @PersistenceCapable
> > > @Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
> > > public abstract class Car {
>
> > >   @PrimaryKey
> > >   @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
> > >   protected Key key;
>
> > >   public Car() {
> > >   }
>
> > > }
>
> > > Model extends Car:
>
> > > @PersistenceCapable
> > > public class Model extends Car {
>
> > >   public Model() {
> > >   }
>
> > > }
>
> > > I load a Person object and create a new Car object, I.e.
>
> > > Person personObj = pm.getObjectById(Person.class, key);
> > > Car newCar = new Model();
>
> > > I then try to persist the new Car by adding it to the "cars" property
> > > in the Person Class, I.e.
>
> > > personObj.getCars().add(newCar);
>
> > > However, when I try this App Engine throws the following exception:
>
> > > java.lang.ArrayIndexOutOfBoundsException: 0
> > >         at
> > > org.datanucleus.store.mapped.scostore.FKListStore.<init>(FKListStore.java:
> > > 133)
> > >         at
> > > org.datanucleus.store.appengine.DatastoreFKListStore.<init>(DatastoreFKList
> > >  Store.java:
> > > 41)
> > >         at
> > > org.datanucleus.store.appengine.DatastoreManager.newFKListStore(DatastoreMa
> > >  nager.java:
> > > 528)
> > >         at
> > > org.datanucleus.store.mapped.MappedStoreManager.getBackingStoreForCollectio
> > >  n(MappedStoreManager.java:
> > > 729)
> > >         at
> > > org.datanucleus.store.mapped.MappedStoreManager.getBackingStoreForField(Map
> > >  pedStoreManager.java:
> > > 646)
> > >         at org.datanucleus.sco.backed.List.<init>(List.java:104)
> > >         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native
> > > Method)
> > >         at
> > > sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAcce
> > >  ssorImpl.java:
> > > 39)
> > >         at
> > > sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstru
> > >  ctorAccessorImpl.java:
> > > 27)
> > >         at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
> > >         at
> > > com.google.appengine.tools.development.agent.runtime.Runtime.newInstance_(R
> > >  untime.java:
> > > 112)
> > >         at
> > > com.google.appengine.tools.development.agent.runtime.Runtime.newInstance(Ru
> > >  ntime.java:
> > > 120)
> > >         at org.datanucleus.util.ClassUtils.newInstance(ClassUtils.java:94)
> > >         at org.datanucleus.sco.SCOUtils.newSCOInstance(SCOUtils.java:164)
> > >         at
> > > org.datanucleus.store.mapped.mapping.AbstractContainerMapping.replaceFieldW
> > >  ithWrapper(AbstractContainerMapping.java:
> > > 426)
> > >         at
> > > org.datanucleus.store.mapped.mapping.CollectionMapping.postInsert(Collectio
> > >  nMapping.java:
> > > 165)
> > >         at
> > > org.datanucleus.store.appengine.DatastoreRelationFieldManager.runPostInsert
> > >  MappingCallbacks(DatastoreRelationFieldManager.java:
> > > 217)
> > >         at
> > > org.datanucleus.store.appengine.DatastoreRelationFieldManager.access
> > > $200(DatastoreRelationFieldManager.java:48)
> > >         at org.datanucleus.store.appengine.DatastoreRelationFieldManager
> > > $1.apply(DatastoreRelationFieldManager.java:116)
> > >         at
> > > org.datanucleus.store.appengine.DatastoreRelationFieldManager.storeRelation
> > >  s(DatastoreRelationFieldManager.java:
> > > 81)
> > >         at
> > > org.datanucleus.store.appengine.DatastoreFieldManager.storeRelations(Datast
> > >  oreFieldManager.java:
> > > 955)
> > >         at
> > > org.datanucleus.store.appengine.DatastorePersistenceHandler.storeRelations(
> > >  DatastorePersistenceHandler.java:
> > > 546)
> > >         at
> > > org.datanucleus.store.appengine.DatastorePersistenceHandler.insertPostProce
> > >  ss(DatastorePersistenceHandler.java:
> > > 304)
> > >         at
> > > org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObjects(D
> > >  atastorePersistenceHandler.java:
> > > 256)
> > >         at
> > > org.datanucleus.store.appengine.DatastorePersistenceHandler.insertObject(Da
> > >  tastorePersistenceHandler.java:
> > > 240)
> > >         at
> > > org.datanucleus.state.JDOStateManagerImpl.internalMakePersistent(JDOStateMa
> > >  nagerImpl.java:
> > > 3185)
> > >         at
> > > org.datanucleus.state.JDOStateManagerImpl.makePersistent(JDOStateManagerImp
> > >  l.java:
> > > 3161)
> > >         at
> > > org.datanucleus.ObjectManagerImpl.persistObjectInternal(ObjectManagerImpl.j
> > >  ava:
> > > 1298)
> > >         at
> > > org.datanucleus.ObjectManagerImpl.persistObject(ObjectManagerImpl.java:
> > > 1170)
> > >         at
> > > org.datanucleus.jdo.JDOPersistenceManager.jdoMakePersistent(JDOPersistenceM
> > >  anager.java:
> > > 669)
> > >         at
> > > org.datanucleus.jdo.JDOPersistenceManager.makePersistent(JDOPersistenceMana
> > >  ger.java:
> > > 694)
>
> > > This sounds much like a similar question that was raised 
> > > here:http://groups.google.com/group/google-appengine-java/browse_thread/th...
>
> > > Has anyone else seen this issue before?
>
> > > Thanks

-- 
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-java@googlegroups.com.
To unsubscribe from this group, send email to 
google-appengine-java+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en.

Reply via email to