On the ActivityReservation model object, there is an instance of
RatePlan (which is also a child object of Activity).

So, here's a thought, I will try it out later, but I thought I'd ask
right now about it:
how about having subclasses of RatePlan that live on different parent
objects?  since RatePlan does not have a pointer up to it's parent, I
think this could work, but I don't know about the underlying
app-engine JDO implementation issues that might cause.  I'm thinking
something like ActivityRatePlan, BundledRatePlan etc. that don't
really add any api/field functionality, but do make it so that only a
Bundle is the parent of a BundledRatePlan, and only Activity is the
parent of ActivityRatePlan etc. etc.  do you think that would cause
any issues?
I'd love it if you could look into allowing more than one parent
object type for a given child object type.  If it's not too much
trouble, it'd really help me out.

in your Person example above, the loadAddress() method probably uses
either a PersistenceManager or some generic wrapper for it?  If that's
the case, I'm wondering how that scales.  I've kind of viewed one of
the main advantages of using JDO/JPA is that the persistence framework
deals with that lazy loading/orphan deletion for you.
I could also make some kind of embedded field that is embedded only, I
think I've had luck with putting those kinds of objects on multiple
parent object without an issue.  That may work for me.

thanks again Max, you've been a big help.
-bryce




On Wed, Dec 9, 2009 at 11:49 AM, Max Ross (Google)
<maxr+appeng...@google.com> wrote:
> I don't see a class that is owned by more than one entity in your latest
> example.  What exactly should I be looking at?
>
> Your understanding of the BigTable layout is correct, but you're bumping
> into a limitation of how we've mapped JDO on to that layout.  The low-level
> api knows nothing about your data model and will allow you to create
> whatever parent/child relationships you want.  However, the low-level api
> also knows nothing about ownership and dependent data.  We need JDO to
> enforce these expectations, and in order to enforce these expectations
> transactionally we need to put owned entities in the same entity group as
> their parents.  We also assume that each child object only has one type of
> parent, and that restriction is causing problems for you.
>
> Why do we have that last restriction?  To be honest, I never considered not
> having it because in a relational database, if you're modeling ownership
> with foreign keys, the foreign key that points to your "owner" typically
> points to a record in a specific table - the schema dictates the type of the
> parent.  With JDO on top of the datastore, however, it does seem like we can
> take advantage of the underlying flexibility.  So long as the relationship
> isn't bidirectional (no parent pointer on the child), you should be able to
> have an object be the child of any number of different parents.  Very
> interesting, thanks for pushing!  I'll file an issue for this.  It might be
> easy or it might be hard, I'm not sure.
>
> So what can you do right now?  Unfortunately you're stuck with unowned
> relationships.  Note, however, that just because you've implemented
> something as an unowned relationship doesn't mean you need to expose it that
> way in your object model.  The datastore doesn't do joins, so something like
> this:
>
> class Person {
>
>   Key addressKey;
>   transient Address address;
>
>   synchronized Address getAddress() {
>     if (address == null) {
>         address = loadAddress();
>     }
>     return address;
>   }
>
> is not actually any less efficient than using an owned relationship.  Yes,
> you have to write more code and it's up to you to ensure that the Address
> gets deleted when the Person gets deleted, but in terms of the code you need
> to write to interact with the model objects it shouldn't be that different.
>
> Hope this helps,
> Max
> On Tue, Dec 8, 2009 at 11:15 PM, bryce cottam <bcot...@gmail.com> wrote:
>>
>> Thanks for filing that Max.
>>
>> I'm kind of interested in your findings because there is another place
>> where I'm doing about the same thing (i.e. making a RatePlan instance
>> a direct child of an Entity other than Activity) and it works fine in
>> that case most of the time.  Sometimes it gives me the "oid is not an
>> instance of javax.jdo.identity.ObjectIdentity" exception.
>>
>> here's the model:
>> @PersistenceCapable(identityType=IdentityType.APPLICATION)
>> public class ActivityReservation extends BaseBean
>> {
>>       �...@primarykey
>>       �...@persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
>>        private Key id;
>>
>>       �...@embedded(
>>                members={
>>                               �...@persistent(name="key",
>> column="activityKey"),
>>                               �...@persistent(name="name", column="name"),
>>                               �...@persistent(name="pricingModel",
>> column="pricingModel")
>>                }
>>        )
>>       �...@persistent(defaultFetchGroup="true")
>>        private ActivityFK activity;
>>
>>       �...@embedded
>>       �...@persistent(defaultFetchGroup="true")
>>        private ActivityLaunchFK launch;
>>
>>       �...@persistent
>>        private RatePlan ratePlan;
>>       �...@persistent
>>        private BigDecimal totalCost;
>>       �...@persistent
>>        private List<Guest> guests = new ArrayList<Guest>();
>> }
>>
>> and
>>
>> @PersistenceCapable(identityType=IdentityType.APPLICATION)
>> public class Reservation extends BaseBean
>> {
>>       �...@primarykey
>>       �...@persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
>>        private Key id;
>>
>>       �...@persistent
>>        private Set<ActivityReservation> activityReservations = new
>> HashSet<ActivityReservation>();
>>
>>       �...@persistent
>>        private Date createdOn;
>>       �...@persistent
>>        private String contactName;
>>       �...@persistent
>>        private BigDecimal totalCost;
>>       �...@persistent
>>        private BigDecimal totalPaid;
>>       �...@persistent
>>        private BigDecimal balanceDue;
>> }
>>
>> I'd really like to avoid using a Key instance rather than a RatePlan
>> instance because I want to ensure that the RatePlan linked to a
>> BundledActivity or an ActivityReservation is not used anywhere else in
>> the system.  If I allow a Key instance there, someone may be tempted
>> to put the Key of an un-owned RatePlan which can be edited out of
>> context.  So, I need to make sure I understand this limitation: if
>> class A has an instance (or instances) of class B in your data model,
>> then class C is not allowed to have an instance (or instances) of
>> class B?  I don't remember reading that in any of the docs, but
>> perhaps I simply missed it.  That's kind of a big deal for me.  I'm
>> almost to the point where I can present my proof of concept to the
>> "higher ups" and then start dedicating time to simply porting our app
>> to the app-engine, but this may make me step back a bit.  Our classes
>> share a lot of data, usually I can get around this with the XyzFK
>> pattern used above (i.e. ActivityFK and ActivityLaunchFK), but those
>> are just foreign keys with denormalized data that allows for either
>> querying filters, or simple field loading of foreign objects without
>> loading the full foreign object.  I thought that the layout of
>> BigTable just made rows of child/dependant objects sequentially after
>> the parent row:
>> Activity(275) {name:"Foo Bar", .... fieldN:valueN...}
>> Activity(275)/RatePlan(123) {name:"foo bar Rate", ..... fieldN:valueN...}
>> Activity(276) {name:"Bar Baz", .... fieldN:valueN...}
>> Activity(276)/RatePlan(124) {name:"bar baz Rate", ..... fieldN:valueN...}
>> .....
>>
>> and that you'd be able to do something like:
>> Bundle(543) {name:"Foo Bar Bundle", .... fieldN:valueN...}
>> Bundle(543)/RatePlan(321) {name:"my Rate", ..... fieldN:valueN...}
>>
>> You can make RatePlan instances without a parent right?  I thought I
>> had a handle on the BigTable layout, but perhaps I am wrong?  It would
>> be really great for me if a particular class could be a field on more
>> than one parent containing class, *not the same instance*, but just
>> the same type.  Is there any other way (besides just using Key
>> instances) you can think of that would accomplish what I am after?
>>
>>
>> thanks again,
>> -bryce
>>
>> On Tue, Dec 8, 2009 at 7:21 PM, Max Ross (Google)
>> <maxr+appeng...@google.com> wrote:
>> > Filed
>> > http://code.google.com/p/datanucleus-appengine/issues/detail?id=170
>> >
>> > On Tue, Dec 8, 2009 at 6:18 PM, Max Ross (Google)
>> > <maxr+appeng...@google.com> wrote:
>> >>
>> >> Ok I think I know what's going on.  First here's a stripped down
>> >> version
>> >> of your object model with the bare minimum needed to reproduce the
>> >> exception:
>> >>
>> >>   @PersistenceCapable(identityType = IdentityType.APPLICATION)
>> >>   public class RatePlan {
>> >>     @PrimaryKey
>> >>     @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
>> >>     private Key id;
>> >>   }
>> >>
>> >>   @PersistenceCapable(identityType = IdentityType.APPLICATION)
>> >>   public class Activity {
>> >>     @PrimaryKey
>> >>     @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
>> >>     private Key id;
>> >>
>> >>     @Persistent
>> >>     private List<RatePlan> ratePlans = new ArrayList<RatePlan>();
>> >>   }
>> >>
>> >>   @PersistenceCapable(identityType = IdentityType.APPLICATION)
>> >>   public class Bundle {
>> >>     @PrimaryKey
>> >>     @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
>> >>     private Key id;
>> >>
>> >>     @Persistent
>> >>     private RatePlan ratePlan;
>> >>
>> >>     public void setRatePlan(RatePlan ratePlan) {
>> >>       this.ratePlan = ratePlan;
>> >>     }
>> >>   }
>> >>
>> >> And here's the unit test I turned your code into that generates the
>> >> exception:
>> >>   public void testBryce() {
>> >>     pm.newQuery(Activity.class).execute();
>> >>
>> >>     Bundle bundle = new Bundle();
>> >>     bundle.setRatePlan(new RatePlan());
>> >>
>> >>     pm.makePersistent(bundle);
>> >>   }
>> >>
>> >> I believe the issue is that RatePlan is owned by two different objects
>> >> in
>> >> your model - Bundle and Activity.  Due to the nature of primary keys in
>> >> the
>> >> app engine datastore, a class can only have a single owner.  We detect
>> >> other
>> >> flavors of this scenario when the entity meta-data is loaded but not
>> >> this
>> >> particular variant.  I'll file a bug and try to produce a useful
>> >> exception.
>> >> To work around this you'll need to either switch Activity.ratePlans or
>> >> Bundle.ratePlans to be an unowned relationship and just store the Key
>> >> of the
>> >> RatePlan rather than the RatePlan itself.  Please give that a try and
>> >> let me
>> >> know how it goes.
>> >>
>> >> Thanks,
>> >> Max
>> >> On Tue, Dec 8, 2009 at 10:19 AM, bryce cottam <bcot...@gmail.com>
>> >> wrote:
>> >>>
>> >>> No worries Max, I'm using 1.2.6 right now, so the "multiple instance"
>> >>> bug
>> >>> isn't an issue right now. Whenever you get to it is fine. As always I
>> >>> appreciate your input.
>> >>>
>> >>> Thanks
>> >>> -bryce
>> >>>
>> >>> On Dec 8, 2009 10:14 AM, "Max Ross (Google)"
>> >>> <maxr+appeng...@google.com>
>> >>> wrote:
>> >>>
>> >>> Hi Bryce,
>> >>>
>> >>> I started digging into you issue and quickly bumped into the "Multiple
>> >>> relationships of the same type" bug for which I posted the
>> >>> workaround.  Then
>> >>> I got bogged down with unrelated stuff.  I have definite plans to get
>> >>> back
>> >>> to your example today.  Thanks for being patient, and sorry this is
>> >>> taking
>> >>> so long.
>> >>>
>> >>> Max
>> >>>
>> >>> On Mon, Dec 7, 2009 at 11:35 PM, bcottam <bcot...@gmail.com> wrote: >
>> >>> >
>> >>> Max, have you had a chance...
>> >>>
>> >>> --
>> >>>
>> >>> 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.
>> >>> For more options, visit this group at
>> >>> http://groups.google.com/group/google-appengine-java?hl=en.
>> >>
>> >
>> > --
>> >
>> > 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.
>> > For more options, visit this group at
>> > http://groups.google.com/group/google-appengine-java?hl=en.
>> >
>>
>> --
>>
>> 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.
>> For more options, visit this group at
>> http://groups.google.com/group/google-appengine-java?hl=en.
>>
>>
>
> --
>
> 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.
> For more options, visit this group at
> http://groups.google.com/group/google-appengine-java?hl=en.
>

--

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.
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en.


Reply via email to