Closed GoogleCodeExporter closed 9 years ago
This seems like a pretty straightforward and necessary capability. I ran into
the same problem. Would very much appreciate some feedback on this issue. Is it
not possible to have the system generate an ID for a top level object? I have a
Person object which references a UserPwd object (contains login information),
an Address object (contains mailing address info), and a collection of IRole
(interface) objects (contains roles played by this Person). I get the same
behavior described above and need a solution to this. I don't see how I can
justify the use of twig if it is not possible to have the system generate an ID
for the top level object. Please help. Thanks.
Original comment by rickh...@gmail.com
on 25 Mar 2011 at 2:28
This is a chicken and egg problem that I am pretty sure JDO does not handle.
You see, the Person contains the Key for the UserPwd so it must have that key
before it can be stored. If the UserPwd is a child of the Person then its key
actually contains the key of the Person which.... hasn't been stored yet. So
the Person must have its @Id set before being stored - then the UsePwd can know
the Key of the parent (Person) without storing it.
Luckily, help is at hand by using the annotation:
@Entity(allocateIdsBy=10)
public class Person {...}
That will tell Twig to get ids from the datastore (unique) and set the
Person.id.
It is not very well tested yet, but hopefully this has explained why the
fundamental problem exists. I am not sure that any other frameworks provide an
equivalent.
Original comment by jdpatterson
on 25 Mar 2011 at 3:41
Thank you for the quick response.
Unless I'm mistaken, JDO does support this. I'm trying to convert an existing
JDO/GAE application. In my existing application, I have several places in which
I store a top-level object that contains child objects within the same entity
group.
More importantly, I still cannot get this to work. I'm running twig-persist
2.0b4, and get the following exception when trying to store Person with the key
annotated as:
@Id String key;
where key == null;
Caused by: java.lang.NullPointerException
at com.google.code.twig.standard.StandardCommonStoreCommand.maybeSetAllocatedId(StandardCommonStoreCommand.java:290)
at com.google.code.twig.standard.StandardCommonStoreCommand.instanceToEntity(StandardCommonStoreCommand.java:251)
at com.google.code.twig.standard.StandardSingleStoreCommand.now(StandardSingleStoreCommand.java:51)
at com.google.code.twig.standard.TranslatorObjectDatastore.store(TranslatorObjectDatastore.java:144)
at com.toolcafe.gwtstore.server.AbstractAdminServiceImpl.createBaseAdmin(AbstractAdminServiceImpl.java:65)
at com.youvoiceit.server.AdminServiceImpl.initServer(AdminServiceImpl.java:20)
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
I thought that perhaps I needed the latest sources, so I "cloned" the source to
my system and tried to build. I get some compile errors and not sure how to fix
them. To build, I copied the sources to an Eclipse Java project, added the
Google Collections JAR, junit-4.9b2, appengine-testing.jar, and referenced the
Google App Engine library. The errors are as follows:
Description Resource Path Location Type
Description Resource Path Location Type
The constructor QueryResultIteratorImpl(AsyncPreparedQuery, List<Entity>,
QueryResultsSourceImpl, FetchOptions, Transaction) is
undefined AsyncPreparedQuery.java /twig-persist/src/com/google/appengine/api/dat
astore line 97 Java Problem
The constructor QueryResultsSourceImpl(null, FetchOptions, Transaction) is
undefined AsyncPreparedQuery.java /twig-persist/src/com/google/appengine/api/dat
astore line 95 Java Problem
The method loadFromPb(DatastorePb.QueryResult) is undefined for the type
QueryResultsSourceImpl AsyncPreparedQuery.java /twig-persist/src/com/google/appe
ngine/api/datastore line 96 Java Problem
The method run() of type new TransactionRunner(){} must override or implement a
supertype
method AsyncDatastoreHelper.java /twig-persist/src/com/google/appengine/api/data
store line 38 Java Problem
The type AsyncPreparedQuery must implement the inherited abstract method
PreparedQuery.countEntities(FetchOptions) AsyncPreparedQuery.java /twig-persist/
src/com/google/appengine/api/datastore line 15 Java Problem
The type new TransactionRunner(){} must implement the inherited abstract method
TransactionRunner.runInternal() AsyncDatastoreHelper.java /twig-persist/src/com/
google/appengine/api/datastore line 35 Java Problem
Thanks,
Rick
Original comment by rickh...@gmail.com
on 25 Mar 2011 at 7:10
I haven't yet gotten a clean build of twig-persist, as described above, but I
decided to do more testing with the latest sources. I tried two alternatives:
Person with @Id annotation associated with the key
@Id String key;
and without the annotation:
String key;
In both cases, I get the same Exception stated in my previous post.
Caused by: java.lang.NullPointerException
at com.google.code.twig.standard.StandardCommonStoreCommand.maybeSetAllocatedId(StandardCommonStoreCommand.java:290)
at com.google.code.twig.standard.StandardCommonStoreCommand.instanceToEntity(StandardCommonStoreCommand.java:251)
at com.google.code.twig.standard.StandardSingleStoreCommand.now(StandardSingleStoreCommand.java:51)
at com.google.code.twig.standard.TranslatorObjectDatastore.store(TranslatorObjectDatastore.java:144)
Original comment by rickh...@gmail.com
on 25 Mar 2011 at 7:46
The latest source has a fix for this issue. Can you check it out to include in
your project?
Original comment by jdpatterson
on 26 Mar 2011 at 3:10
Sorry, JD. Still not working. I continue to get the same exception when I try
to store the Person object referencing a UserPwd, Administrator (via a
collection), and an Address -- when Person's @Id private String key == null.
I'm probably doing something wrong, but not sure what.
Caused by: java.lang.NullPointerException
at com.google.code.twig.standard.StandardCommonStoreCommand.maybeSetAllocatedId(StandardCommonStoreCommand.java:290)
at com.google.code.twig.standard.StandardCommonStoreCommand.instanceToEntity(StandardCommonStoreCommand.java:251)
at com.google.code.twig.standard.StandardSingleStoreCommand.now(StandardSingleStoreCommand.java:51)
at com.google.code.twig.standard.TranslatorObjectDatastore.store(TranslatorObjectDatastore.java:144)
Would you like me to send you the relevant code? If so, what's the best way to
do so?
Original comment by rickh...@gmail.com
on 26 Mar 2011 at 3:21
First, are you sure you have the latest source from hg? Do you perhaps have a
jar in the /WEB-INF/lib dir?
Line 290 is where the fix went in and I can't see that it could possibly throw
a NPE:
http://code.google.com/p/twig-persist/source/browse/src/main/java/com/google/cod
e/twig/standard/StandardCommonStoreCommand.java?r=v2.0#290
Original comment by jdpatterson
on 26 Mar 2011 at 4:26
Its always the classpath! :-) Yes, I forgot to remove the old JAR! My
apologies. BUT, it still isn't working. When I set the Person's id field prior
to calling store(), store() works, but when I do not, I get the following
exception:
Caused by: java.lang.IllegalStateException: Key specification is incomplete.
You may need to define a key name for this or its parent instance
at com.vercer.engine.persist.standard.KeySpecification.toKey(KeySpecification.java:96)
at com.vercer.engine.persist.standard.ChildEntityTranslator.getParentKey(ChildEntityTranslator.java:19)
at com.vercer.engine.persist.standard.EntityTranslator$2.get(EntityTranslator.java:121)
at com.vercer.engine.persist.standard.EntityTranslator$2.get(EntityTranslator.java:1)
at com.vercer.engine.persist.standard.StrategyObjectDatastore.dereferencePropertyValue(StrategyObjectDatastore.java:987)
at com.vercer.engine.persist.standard.StrategyObjectDatastore.transferProperties(StrategyObjectDatastore.java:970)
at com.vercer.engine.persist.standard.StrategyObjectDatastore.instanceToEntity(StrategyObjectDatastore.java:920)
at com.vercer.engine.persist.standard.StrategyObjectDatastore.internalStore(StrategyObjectDatastore.java:668)
at com.vercer.engine.persist.standard.StrategyObjectDatastore.store(StrategyObjectDatastore.java:658)
at com.vercer.engine.persist.standard.StrategyObjectDatastore.store(StrategyObjectDatastore.java:1011)
at com.toolcafe.gwtstore.server.AbstractAdminServiceImpl.createBaseAdmin(AbstractAdminServiceImpl.java:69)
Please see the attached file for a segment of Person and my code used to store
it.
Original comment by rickh...@gmail.com
on 26 Mar 2011 at 5:32
Attachments:
Well, progress is being made at least. Its a new exception :)
I had only briefly tested this with Child instances - not parent instances.
Seeing as I don't have a test case yet for this (hint hint patch welcome) could
you apply this patch and see if it fixes your problem. It basically just moves
a single line
diff -r 6d844455e808
src/main/java/com/google/code/twig/standard/StandardCommonStoreCommand.java
---
a/src/main/java/com/google/code/twig/standard/StandardCommonStoreCommand.java Th
u Mar 24 14:25:25 2011 +0700
+++
b/src/main/java/com/google/code/twig/standard/StandardCommonStoreCommand.java Su
n Mar 27 00:49:24 2011 +0700
@@ -156,7 +156,7 @@
Map<T, Entity> entities = new LinkedHashMap<T, Entity>(instances.size());
if (batch)
{
- // indicates that all entities should be collected for one put
+ // all entities will be collected for one bulk put
datastore.batched = (Map<Object, Entity>) entities;
}
@@ -239,6 +239,8 @@
datastore.keyCache.cacheKeyReferenceForInstance(instance, datastore.encodeKeySpec.toObjectReference());
}
+ maybeSetAllocatedId(instance);
+
// translate fields to properties - sets key parent and id
PropertyTranslator encoder = datastore.encoder(instance);
Set<Property> properties = encoder.encode(instance, Path.EMPTY_PATH, datastore.indexed);
@@ -246,8 +248,6 @@
{
throw new IllegalStateException("Could not translate instance: " + instance);
}
-
- maybeSetAllocatedId(instance);
// the key will now be set with id and parent
Entity entity = createEntity();
Original comment by jdpatterson
on 26 Mar 2011 at 5:51
btw, I'm liking twig very much. It looks like I'll be able to remove lots of
JDO cruft (due to the feeble GAE implementation). Thank you for helping with
this problem.
I'm confused, though. Your patch references:
com/google/code/twig/standard/StandardCommonStoreCommand.java
whereas the source code I've downloaded uses package:
com.vercer.engine.persist.*
The code I downloaded came from:
http://code.google.com/p/twig-persist/source/browse/#hg%2Fsrc%2Fshared%2Fcom%2Fv
ercer
via:
hg clone https://twig-persist.googlecode.com/hg/ twig-persist
Do I have the right code?
Thanks,
Rick
Original comment by rickh...@gmail.com
on 26 Mar 2011 at 8:58
Ah-ha!! You've got the v1.0 branch :)
When you do your "pull" you have to choose "v2.0" from the small drop down -
not very clear unfortunately. Give that a shot and get back to me.
Original comment by jdpatterson
on 27 Mar 2011 at 4:15
Sorry that drop down is on the google site for browsing the code. When you
pull the code you get all changesets (both branches) and you need to change
branch.
hg branches
hg branch v2.0
I've just checked in a test case that shows allocateIdsBy working on a parent
class:
http://code.google.com/p/twig-persist/source/browse/src/test/java/com/google/cod
e/twig/tests/unit/StoreCommandTest.java
Original comment by jdpatterson
on 27 Mar 2011 at 6:09
John, we're making progress. I figured the latest code would be the default,
but assuming sometimes gets you in trouble. :-) I now have the V2.0 code, built
with Maven, and have done some more testing. twig is very cool, very nice
programming model. I have uncovered a couple of problems. Appreciate it if you
could look into these.
1. I still can't get automatic id generation working. The reason appears to be
that I am using a String as the ID type. Here's the exception.
Caused by: java.lang.IllegalStateException: You must set an id value if the id
field is not a numeric type.
at com.google.code.twig.standard.StandardCommonStoreCommand.setInstanceId(StandardCommonStoreCommand.java:103)
at com.google.code.twig.standard.StandardSingleStoreCommand.now(StandardSingleStoreCommand.java:60)
at com.google.code.twig.standard.TranslatorObjectDatastore.store(TranslatorObjectDatastore.java:145)
at com.toolcafe.gwtstore.server.AbstractAdminServiceImpl.createBaseAdmin(AbstractAdminServiceImpl.java:69)
I'd like to keep by IDs defined as Strings (unless there's a really good reason
not to) as I have quite a lot of code dependent on these definitions, and don't
really want to hack them all out. Making String IDs work seems like it would be
a straightforward fix. Is that the case, and if so, can you please fix this?
2. @Entity doesn't seem to honor the "kind" attribute. I have the following
code:
@Entity(allocateIdsBy=10, kind="Person")
...but the entity kind generated contains the Java package name. Seems like a
bug.
Thanks again,
Rick
Original comment by rickh...@gmail.com
on 27 Mar 2011 at 4:52
in reverse order...
2. yes this is an incomplete feature sorry. I'm working on getting this done
in a branch that has a few broken bits. If you create an issue for it you will
be notified as soon as it is checked in. For now please use
DefaultConfiguration.registerTypeName(Class, String)
I am working on a DefaultConfiguration.register(Classs) and have plans to add a
package scanner that will discover entity classes.
1. Not really... you see the id is set normally after being generated on the
server (always a long). Previously the code did what you are asking: it would
convert to String. But then when using the instance again it would not be
found because the auto-generated id e.g. 5 != "5". This caused a subtle bug
where update() created a duplicate entity.
To add this ability would require special case code for pre-allocated ids.
Probably not worth the complication when you can easily do the conversion to
String in your data model getId():String. Remember Twig access fields
directly, not though setters.
Original comment by jdpatterson
on 27 Mar 2011 at 5:46
Original issue reported on code.google.com by
barbedwi...@gmail.com
on 6 Nov 2010 at 9:16