Polymorphism in Objectify: "id cannot be 0" in the SaveException

5 views
Skip to first unread message

pulp_fiction via StackOverflow

unread,
Dec 28, 2016, 1:19:04 AM12/28/16
to google-appengin...@googlegroups.com

I am building an Android App with the backend hosted on GAE. Right now I am trying to persist a POJO entity through Objectify to the Datastore. Following are the POJOs I am trying to persist.

@Entity
public class GenericLearnerProfile {

    private String mName;
    @Id
    private String mEmailID;
    private String mPhoneNo;
    private String mImagePath;
    private String mCurrentStatus;
    private String mPassword;

    //Default constructor required for persistence(serialization)
    public GenericLearnerProfile(){}
    //.......Other Getters, Setters and constructors follow
}

@Entity
public class TutorProfile extends GenericLearnerProfile {

    private EducationalQualification[] educationalQualifications;
    private Occupation occupation;

    //Default constructor required for persistence(serialization)
    public TutorProfile(){}
    //.......Other Getters, Setters and constructors follow
}

As you can see, its a pretty simple hierarchy.

Problem: When I try to persist just GenericLearnerProfile, it successfully happens but with TutorProfile, it throws out the following exception:

com.googlecode.objectify.SaveException: Error saving com.learncity.backend.persistence.TutorProfile@22fc69b4: id cannot be zero
    at com.googlecode.objectify.impl.EntityMetadata.save(EntityMetadata.java:95)
    at com.googlecode.objectify.impl.WriteEngine.save(WriteEngine.java:73)
    at com.googlecode.objectify.impl.SaverImpl.entities(SaverImpl.java:60)
    at com.googlecode.objectify.impl.SaverImpl.entity(SaverImpl.java:35)
    at com.learncity.backend.persistence.TutorProfileEndpoint.insert(TutorProfileEndpoint.java:83)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:130)
    at com.google.api.server.spi.SystemService.invokeServiceMethod(SystemService.java:363)
    at com.google.api.server.spi.SystemServiceServlet.execute(SystemServiceServlet.java:113)
    at com.google.api.server.spi.SystemServiceServlet.doPost(SystemServiceServlet.java:71)
    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.googlecode.objectify.ObjectifyFilter.doFilter(ObjectifyFilter.java:48)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:128)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:63)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:50)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:366)
    at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:349)
    at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116)
    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.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:98)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:513)
    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: id cannot be zero
    at com.google.appengine.api.datastore.KeyFactory.createKey(KeyFactory.java:52)
    at com.google.appengine.api.datastore.KeyFactory.createKey(KeyFactory.java:47)
    at com.googlecode.objectify.util.DatastoreUtils.createKey(DatastoreUtils.java:77)
    at com.googlecode.objectify.impl.KeyMetadata.getRawKey(KeyMetadata.java:187)
    at com.googlecode.objectify.impl.translate.EntityCreator.save(EntityCreator.java:54)
    at com.googlecode.objectify.impl.translate.EntityCreator.save(EntityCreator.java:16)
    at com.googlecode.objectify.impl.translate.ClassTranslator.saveSafe(ClassTranslator.java:136)
    at com.googlecode.objectify.impl.translate.ClassTranslator.saveSafe(ClassTranslator.java:21)
    at com.googlecode.objectify.impl.translate.NullSafeTranslator.save(NullSafeTranslator.java:25)
    at com.googlecode.objectify.impl.translate.ArrayTranslatorFactory$1.save(ArrayTranslatorFactory.java:83)
    at com.googlecode.objectify.impl.translate.ArrayTranslatorFactory$1.save(ArrayTranslatorFactory.java:39)
    at com.googlecode.objectify.impl.PropertyPopulator.save(PropertyPopulator.java:135)
    at com.googlecode.objectify.impl.translate.ClassPopulator.save(ClassPopulator.java:153)
    at com.googlecode.objectify.impl.translate.ClassTranslator.saveSafe(ClassTranslator.java:138)
    at com.googlecode.objectify.impl.translate.ClassTranslator.saveSafe(ClassTranslator.java:21)
    at com.googlecode.objectify.impl.translate.NullSafeTranslator.save(NullSafeTranslator.java:25)
    at com.googlecode.objectify.impl.EntityMetadata.save(EntityMetadata.java:89)
    ... 51 more

Note:

  • All the embedded objects have long as an Id.
  • It doesn't help if I make them Long. It then displays the error - "id cannot be null"
  • I don't want to manually set the long ids for the embedded objects because it really doesn't make sense for the classes to necessarily have an id(other than for persistence)
  • The strange thing is that, GenericLearnerProfile is getting persisted.

Can anyone help me understand this exception and way around it?



Please DO NOT REPLY directly to this email but go to StackOverflow:
http://stackoverflow.com/questions/41356400/polymorphism-in-objectify-id-cannot-be-0-in-the-saveexception

pulp_fiction via StackOverflow

unread,
Dec 28, 2016, 2:49:04 AM12/28/16
to google-appengin...@googlegroups.com

Can anyone help me understand this exception and way around it?

pulp_fiction via StackOverflow

unread,
Dec 28, 2016, 3:24:04 AM12/28/16
to google-appengin...@googlegroups.com

The problem is with the expectation that Ids are autogenerated- They do but NOT for embedded entities. Source: https://groups.google.com/forum/#!topic/objectify-appengine/TC2G5I-GJ8w

So as @stickfigure mentioned here:

You have two options:

  1. Don't give your embedded object an id. Don't give it @Entity and don't give it an id field (or at least eliminate @Id). It's just a POJO. 90% of the time, this is what people want with embedded objects.
  2. Allocate the id yourself with the allocator, typically in your (non-default) constructor.

And,

I suggest going one step further and never use automatic id generation for any entities ever. Always use the allocator in the (non-default) constructor of your entities. This ensures that entities always have a valid, stable id. If you always allocate the id before a transaction start, it fixes duplicate entities that can be created when a transaction gets retried. Populating null ids is just a bad idea all around and really should not have been added to GAE.

Also, Riley says,

From http://code.google.com/p/objectify-appengine/wiki/IntroductionToObjectify#Entity_Representation , it doesn't look like @Embedded entities are actually separate entities in your datastore at all. They're just stored as properties directly in fields of the parent object.

If you want the Child entities to be their own, separately-accessible objects, you should change Parent to hold an array of Keys or IDs, and then instantiate your Child objects separately. That'll generate ids for each Child (which you can store in the id array in the Parent).

I solved my problem by making the Embedded objects as Non-entities and therefore no requirement of an ID for them - I removed the @Entity annotation from them and now it is working smoothly.



Please DO NOT REPLY directly to this email but go to StackOverflow:
http://stackoverflow.com/questions/41356400/polymorphism-in-objectify-id-cannot-be-0-in-the-saveexception/41358027#41358027
Reply all
Reply to author
Forward
0 new messages