This should not happen. The instance of B will not be stored again if
it is already persistent (already stored).
Perhaps you are using a different ObjectDatastore to store B first, then
store A? You must either use the same OD or associate B with the new OD
by calling datastore.associate(myB).
>
> In JDO, I would have addressed this problem by storing the datastore
> key for B in A, instead of storing a reference to B.
This is exactly what Twig does. A reference to B is just the Key of B.
> In some cases, I would also store a non-persistent reference to B in A,
hmm, not really sure what you mean here.
> It seems to me that it should be possible for twig to recognize that
> A and B are in two different entity groups (given that B has a key
> assigned previously) and to store B's key in entity A in the
> datastore. But it doesn't seem to work that way.
>
This is exactly what Twig does.
Right now I am completely confused. I really cannot tell what is
happening when from the code posted because I cannot know which line
throws the exception or how the instances are created.
All I can tell from the error message is that the instance of Activist
with id 259 and Person with id 13658 are being stored in the same
transaction. This means that one of them was not loaded or associated
with the current ObjectDatastore.
Yes, you must have an id if you want to associate an instance unless you
use the form associate(Object, Key).
> But, I have to tell you, this was not at all clear (at least to me)
> from the documentation.
I've just written some docs explaining this in a bit more detail. Can
you tell me if this would have helped you? See the bit about associating
http://code.google.com/p/twig-persist/wiki/Lifecycle
> I thought this might be the case because I didn't understand how twig
> could associate an object without having an database key as part of
> the object.
It needs all of the details required to create the key - @Id and @Parent
or @GaeKey (the whole Key; a new feature)
When your model does not have this you must use the other form where you
pass in the Key explicitly.
> The whole thing about twig managing ids didn't (and doesn't) make
> complete sense to me. Here's why.
>
> AFAIK, GAE doesn't guarantee that subsequent requests from a client
> will be processed by the same server. Hence, an object -> id cache is
> not guaranteed to contain an entry for an object previously loaded
> from the database by a given client.
ObjectDatstore is not thread-safe and should not be kept on the server.
You must create a new one for every request - or even every block of
operations. Basically, when ever you need one, create one.
> Therefore, it seems to me (correct me if I'm wrong) that if you want
> to reference an object in another entity group from an object that you
> want to store in the datastore (as in my situation with Person,
> VotingDistrict, and VotingDistrictComp), the referenced object(s) need
> to contain an @Id id as part of their class definition. Otherwise, one
> cannot associate these objects, and will run into the problem that I
> ran into.
Many applications do not use GWT and so do not need to associate
external instances with a new datastore. That is, they load some data,
modify it and store it all in one group of operations.
When you have an application that needs to work on external data (like
yours) it is much more convenient to have an @Id field in the instance.
>
> I don't understand what twig's id management buys you. I have no
> problem using @Id id on my objects and carrying around a database id
> with each object. In fact, I would PREFER to do so.
It allows you to not define an @Id field when you don't need to. As
above, many apps do not need to assoiciate external data instances.
Traditional web apps would send an id to the server, then the server
would load the instance from the datastore and update it.
Also, not requiring an @Id means that classes can be stored without
being designed for it. Just normal Pojos that you may not even have
the source code for. They can still be persisted with Twig and their
properties queried on.
And lastly, having an internal record of which instances are already
persistent means that Twig can enforce the semantic difference between
`store` and `update` which is a great help in preventing programming errors.
> In fact, I will go one step further. I would PREFER to have an
> annotation that allowed me to specify @Key (I think this is now
> deprecated and I don't quite know what it does), but I would like an
> @Key annotation that would ask twig to store the associated instance
> variable with the full datastore key.
This is available using the @GaeKey annotation. Eventually, I would
like to use @Key for this (and deprecate @GaeKey) but @Key was used in
v1.0 where @Id is used now.
> Why, you might ask? Simple. Let's say I have the following objects:
>
> class A {
> B child;
> }
>
> class B { }
>
> Suppose I want to update an instance of B in my client. I may want to
> only transfer B to the client for editing and then transfer it back to
> the server to do od.update(b). But without a full key in b, I need to
> also transfer A to the client and back in order to associate b. (I
> need the b's parent instance to do the associate).
There are actually workarounds for this so that you only need the parent
id and the child data. This is the minimum you could possibly get away
with - even the Key has these details encoded in it (and more cruft).
Hmmm... I've just added the work around to the wiki too. Let me know if
it is clear enough.
> This will be a very big deal for me. I expect to have some pretty
> large object graphs in the datastore, and I don't want to have to
> transfer the whole graph around in order to operate on one leaf.
> Without having something like the @Key I describe above, I will have
> to initialize these objects in my code, which is just cruft that does
> not need to be there.
>
> twig provides a very nice programming model. So much better than JDO.
> But your attempts to make the datastore keys and ids transparent --
> well, unless I'm missing something (and it wouldn't be the first
> time!) are to me, more of a negative.
I hope the new features in v2.0 given above help with this. They allow
you to choose to use implicit Key management or do it yourself when it
makes sense.
Thanks for the feedback!
John