How to store objects in different entity groups

49 views
Skip to first unread message

Rick Horowitz

unread,
Apr 3, 2011, 10:32:27 AM4/3/11
to twig-p...@googlegroups.com
I'm a bit confused about how to reference an entity not in the same entity group. Suppose I have 2 entities, A and B.

public class A {
  @Id long id;
  B b;
}

public class B {
  @Id long id;
}

I can store an instance of B. No problem. If I then create an instance of A (in a new transaction), and reference the previously stored B instance which I have loaded from the datastore, I get an error that indicates that I cannot store instances from more than one entity group within a transaction. 

In JDO, I would have addressed this problem by storing the datastore key for B in A, instead of storing a reference to B. In some cases, I would also store a non-persistent reference to B in A, so I would have a convenient way to transfer A to my GWT client, and be able to get a reference to B on the client. I'm wondering what the best approach is to this problem in twig. Do I store the key for B in A? Same as with JDO? Or is it somehow possible to store a reference to B in A, and actually have it work when I store instance A? 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. 

Help please. Thanks.


John Patterson

unread,
Apr 3, 2011, 1:42:12 PM4/3/11
to twig-p...@googlegroups.com
On 03/04/2011 21:32, Rick Horowitz wrote:
>
> I can store an instance of B. No problem. If I then create an instance
> of A (in a new transaction), and reference the previously stored B
> instance which I have loaded from the datastore, I get an error that
> indicates that I cannot store instances from more than one entity
> group within a transaction.

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.

Rick Horowitz

unread,
Apr 3, 2011, 8:53:59 PM4/3/11
to twig-p...@googlegroups.com
Hey John. I'm still not getting this to work, but I'm very happy to hear that twig should support this. Here's the relevant part of my object model:

public class Person {
  @Id long id;
  ArrayList<IRole> roles;
}

public interface IRole { }

public class Activist implements IRole {
  @Id long id;
  VotingDistrict votingDistrict;
}

VotingDistrict is stored in an earlier transaction with a different ObjectDatastore instance.

I try to store the Person instance, as follows: (I've tried a few variations, but this is the most recent)

ObjectDatastore ds = new AnnotationObjectDatastore();
Transaction txn = ds.beginTransaction();
try {
Activist activist = (Activist)person.getRole(RoleType.activist);
VotingDistrict vd = activist.getVotingDistrict();
ds.associate(vd);
ds.update(vd);
if (ServerUtil.getInstance().isEmpty(person.getKey())) {
ds.associate(person);
ds.update(activist);
} else {
ds.store().instance(person).now();
}
txn.commit();
return person;
} finally {
if (txn.isActive()) {
txn.rollback();
}
}

I get the following exception:

[ERROR] javax.servlet.ServletContext log: Exception while dispatching incoming RPC call
com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract com.toolcafe.gwtstore.shared.model.Person com.youvoiceit.client.rpc.ActivistService.saveActivist(com.toolcafe.gwtstore.shared.model.Person)' threw an unexpected exception: java.lang.IllegalArgumentException: can't operate on multiple entity groups in a single transaction. found both Element {
  type: "com_youvoiceit_shared_model_VotingDistrict"
  id: 259
}
 and Element {
  type: "com_toolcafe_gwtstore_shared_model_Person"
  id: 13658
}

at com.google.gwt.user.server.rpc.RPC.encodeResponseForFailure(RPC.java:385)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:588)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:208)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:58)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:70)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:351)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: java.lang.IllegalArgumentException: can't operate on multiple entity groups in a single transaction. found both Element {
  type: "com_youvoiceit_shared_model_VotingDistrict"
  id: 259
}
 and Element {
  type: "com_toolcafe_gwtstore_shared_model_Person"
  id: 13658
}

at com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:36)
at com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException(DatastoreApiHelper.java:98)
at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:69)
at com.google.appengine.api.datastore.FutureHelper$CumulativeAggregateFuture.get(FutureHelper.java:136)
at com.google.appengine.api.datastore.FutureHelper$TxnAwareFuture.get(FutureHelper.java:213)
at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:67)
at com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:71)
at com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:32)
at com.google.appengine.api.datastore.DatastoreServiceImpl.put(DatastoreServiceImpl.java:101)
at com.google.code.twig.standard.BaseObjectDatastore.servicePut(BaseObjectDatastore.java:51)
at com.google.code.twig.standard.StandardCommonStoreCommand.entityToKey(StandardCommonStoreCommand.java:336)
at com.google.code.twig.standard.StandardSingleStoreCommand.now(StandardSingleStoreCommand.java:57)
at com.google.code.twig.standard.RelationTranslator.instanceToKey(RelationTranslator.java:164)
at com.google.code.twig.standard.RelationTranslator$2.get(RelationTranslator.java:151)
at com.google.code.twig.standard.RelationTranslator$2.get(RelationTranslator.java:148)
at com.google.code.twig.standard.StandardEncodeCommand.dereferencePropertyValue(StandardEncodeCommand.java:58)
at com.google.code.twig.standard.StandardEncodeCommand.transferProperties(StandardEncodeCommand.java:41)
at com.google.code.twig.standard.StandardCommonStoreCommand.instanceToEntity(StandardCommonStoreCommand.java:256)
at com.google.code.twig.standard.StandardSingleStoreCommand.now(StandardSingleStoreCommand.java:51)
at com.google.code.twig.standard.StandardSingleStoreCommand.now(StandardSingleStoreCommand.java:13)
at com.youvoiceit.server.ActivistServiceImpl.saveActivist(ActivistServiceImpl.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:100)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:569)
... 30 more

com.google.gwt.user.client.rpc.StatusCodeException: 500 The call failed on the server; see server log for details
at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:209)
at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:395)
at sun.reflect.GeneratedMethodAccessor33.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:326)
at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:207)
at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:129)
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:214)
at sun.reflect.GeneratedMethodAccessor21.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:281)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:531)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:352)
at java.lang.Thread.run(Thread.java:636)


John Patterson

unread,
Apr 4, 2011, 12:28:46 AM4/4/11
to twig-p...@googlegroups.com
On 04/04/2011 07:53, Rick Horowitz wrote:
> VotingDistrict is stored in an earlier transaction with a different
> ObjectDatastore instance.
>
This is probably the issue. The current OD does not know the
VotingDistrict instance so it stores it again. You need to tell Twig
that the instance is already persistent by calling datastore.associate(vd).


Rick Horowitz

unread,
Apr 4, 2011, 7:45:14 AM4/4/11
to twig-p...@googlegroups.com
I did call ds.associate(vd). I forgot to note that Person contains the @Child annotation for private ArrayList<IRole> roles, and Activist contains @Parent for private Person person. See my previous post for the rest of the relevant object model.

John Patterson

unread,
Apr 4, 2011, 9:25:17 AM4/4/11
to twig-p...@googlegroups.com
On 04/04/2011 18:45, Rick Horowitz wrote:
> I did call ds.associate(vd). I forgot to note that Person contains the
> @Child annotation for private ArrayList<IRole> roles, and Activist
> contains @Parent for private Person person. See my previous post for
> the rest of the relevant object model.

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.

John Patterson

unread,
Apr 4, 2011, 9:26:31 AM4/4/11
to twig-p...@googlegroups.com
BTW, when you debug you can see if an instance is associated by calling
datastore.associatedKey(Object)

Rick Horowitz

unread,
Apr 4, 2011, 11:10:21 AM4/4/11
to twig-p...@googlegroups.com
Let me give you some more information:

1. I added:

Key vdKey = ds.associatedKey(vd);

vdKey.kind = com_youvoiceit_shared_model_VotingDistrict
vdKey.id = 260

This is from the code:

@Override
public Person saveActivist(Person person) {
ObjectDatastore ds = new AnnotationObjectDatastore();
Transaction txn = ds.beginTransaction();
try {
Activist activist = (Activist)person.getRole(RoleType.activist);
VotingDistrict vd = activist.getVotingDistrict();
ds.associate(vd);
Key vdKey = ds.associatedKey(vd);
ds.update(vd);
if (ServerUtil.getInstance().isEmpty(person.getKey())) {
ds.associate(person);
ds.update(activist);
} else {
ds.store().instance(person).now(); // THE EXCEPTION HAPPENS HERE
}
txn.commit();
return person;
} finally {
if (txn.isActive()) {
txn.rollback();
}
}
}

2. The exception happens on the call to now(), in the above code. Before the exception is thrown, 

3. I have a hunch. Could be off base, but is it possible that VotingDistrictComp has something to do with this. This is a child of VotingDistrict (defined as an inner class of VotingDistrict). The reason I'm wondering if this is the case is that this value is processed as an ObjectReference in StandardEncodeCommand.dereferencePropertyValue(Object value) at the following line:

if (value instanceof ObjectReference<?>)
{
value = ((ObjectReference<?>)value).get(); // VotingDistrictComp is processed HERE.
}

3. I've attached several of the classes involved with this problem.

Thanks for your help with this.

Rick
twig-bug-files.zip

John Patterson

unread,
Apr 4, 2011, 11:19:38 AM4/4/11
to twig-p...@googlegroups.com
Put a breakpoint in RelationTranslator.instanceToKey() and you will see when an instance is not found in the cache - it will then be stored.  You are expecting only one instance to not be found in the cache.

Rick Horowitz

unread,
Apr 4, 2011, 1:22:36 PM4/4/11
to twig-p...@googlegroups.com
Solved! :-)

Your suggestion helped me figure out what was going on. btw, I also put a breakpoint in RelationTranslator.instancesToKeys() to catch those objects, as well.

Here's what I learned and how I fixed the problem. (note: I think you should include this writeup in some fashion in your docs):

VotingDistrict (vd) is the parent of VotingDistrictComp (vdc). I did an ObjectDatastore.associate(vd), but I discovered that od was trying to write vdc to the database. So I tried to do od.associate(vdc, vd). This didn't work because vdc did not contain an id. So I added @Id id to VotingDistrictComp. Now I was able to associate the collection of VDCs, with vd as the parent. 

But, I have to tell you, this was not at all clear (at least to me) from the documentation. 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. 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. 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.

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. 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. 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). 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.

Thanks again for your help. I now have this working, but I look forward to your comments regarding @Key being used by twig to initialize an instance variable with the full datastore key.

Cheers,

Rick

P.S. I also needed to remove ds.update(vd) from my code, as it was (correctly) causing an exception due to trying to update > 1 entity from different entity groups in a single txn.

John Patterson

unread,
Apr 6, 2011, 3:30:29 AM4/6/11
to twig-p...@googlegroups.com
On 05/04/2011 00:22, Rick Horowitz wrote:
> ObjectDatastore.associate(vd), but I discovered that od was trying to
> write vdc to the database. So I tried to do od.associate(vdc, vd).
> This didn't work because vdc did not contain an id. So I added @Id id
> to VotingDistrictComp. Now I was able to associate the collection of
> VDCs, with vd as the parent.

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

Rick Horowitz

unread,
Apr 6, 2011, 2:13:08 PM4/6/11
to twig-p...@googlegroups.com
John, thanks for clarifying all that you did. The lifecycle explanation is much better now, though I have a couple of suggestions that may help improve it further.
 
1 I think you may want to add a short section that discusses the different architectural models and their implications for twig. Specifically, the GWT or other AJAX model vs the traditional web model. This is what threw me off at first -- I just didn't even consider the traditional web model, as I haven't worked in that mode in over a year. Now that you point this out, I fully understand and appreciate the value of twig managing IDs and objects in the OD cache.
 
2. It would be useful to give a "best practices" explanation for each of the above architectures, as well as the scenario I brought up earlier of wanting to operate on a single object in a client (eg. GWT app) of a large persistent object graph, and how to best handle this. My understanding of this is that if I want to only transmit the single object back and forth to the client and then back to the server, I should use @GaeKey in that object. This will allow me to associate the object with a new OD instance when it is transmitted back to the server from the client.  Is this correct?
 
3. You may want to have a little section that distinguishes @Id, @Parent, and @GaeKey, very explicitly showing how an object with @Id needs @Parent's up to the top level object, whereas an object with @GaeKey can be associated directly with no @Parent or parent object present.
 
Great work on twig. I'll be happy to help any time I am able. One more request/suggestion. When you get time, please javadoc the code. It would make it easier for semi-initiates like myself, who don't really want to do development on a persistence layer to actually do code modifications and submissions.
 
Thanks.
 
Rick 
 
Reply all
Reply to author
Forward
0 new messages