Objectify 中的多态性:SaveException 中的 "id cannot be 0"
Polymorphism in Objectify: "id cannot be 0" in the SaveException
我正在构建一个 Android 应用程序,后端托管在 GAE 上。现在我正试图通过 Objectify 将 POJO 实体持久化到数据存储区。以下是我试图坚持的 POJO。
@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
}
如您所见,它的层次结构非常简单。
问题: 当我尝试只坚持 GenericLearnerProfile
时,它成功发生了,但是对于 TutorProfile,它抛出了以下异常:
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.save(ArrayTranslatorFactory.java:83)
at com.googlecode.objectify.impl.translate.ArrayTranslatorFactory.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
注:
- 所有嵌入对象都有一个Id。
- 长了也没用。然后显示错误 - "id cannot be null"
- 我不想手动设置嵌入对象的长 ID,因为 类 必须有一个 ID(除了持久性)确实没有意义
任何人都可以帮助我理解这个异常并解决它吗?
问题在于 ID 是自动生成的预期 - 它们会自动生成但不适用于嵌入式实体。
资料来源:https://groups.google.com/forum/#!topic/objectify-appengine/TC2G5I-GJ8w
正如@stickfigure 提到的那样here:
You have two options:
- 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.
- Allocate the id yourself with the allocator, typically in your (non-default) constructor.
并且,
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.
此外,莱利说,
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).
我通过将嵌入式对象设为非实体解决了我的问题,因此不需要它们的 ID - 我从它们中删除了 @Entity 注释,现在它运行顺利。
我正在构建一个 Android 应用程序,后端托管在 GAE 上。现在我正试图通过 Objectify 将 POJO 实体持久化到数据存储区。以下是我试图坚持的 POJO。
@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
}
如您所见,它的层次结构非常简单。
问题: 当我尝试只坚持 GenericLearnerProfile
时,它成功发生了,但是对于 TutorProfile,它抛出了以下异常:
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.save(ArrayTranslatorFactory.java:83)
at com.googlecode.objectify.impl.translate.ArrayTranslatorFactory.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
注:
- 所有嵌入对象都有一个Id。
- 长了也没用。然后显示错误 - "id cannot be null"
- 我不想手动设置嵌入对象的长 ID,因为 类 必须有一个 ID(除了持久性)确实没有意义
任何人都可以帮助我理解这个异常并解决它吗?
问题在于 ID 是自动生成的预期 - 它们会自动生成但不适用于嵌入式实体。 资料来源:https://groups.google.com/forum/#!topic/objectify-appengine/TC2G5I-GJ8w
正如@stickfigure 提到的那样here:
You have two options:
- 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.
- Allocate the id yourself with the allocator, typically in your (non-default) constructor.
并且,
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.
此外,莱利说,
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).
我通过将嵌入式对象设为非实体解决了我的问题,因此不需要它们的 ID - 我从它们中删除了 @Entity 注释,现在它运行顺利。