MikO
MikO

Reputation: 18741

Error creating an entity in Google Datastore with JDO

I'm creating a web application in Google App Engine with JDO and I get a JDOFatalUserException:

javax.jdo.JDOFatalUserException: Detected attempt to establish User(15) as the 
parent of SessionToken("VALID_USER_TOKEN") but the entity identified 
by SessionToken("VALID_USER_TOKEN") has already been persisted 
without a parent.  A parent cannot be established or changed once an 
object has been persisted.

I have the class User, which has a SessionToken. I want to store both User and a SessionToken in the Datastore. The User key is generated automatically, while the SessionToken key is generated by me.

@PersistenceCapable
public class User {

  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Key key;
  @Persistent
  private SessionToken sessionToken;

  //getters and setters...
}

And:

@PersistenceCapable
public class SessionToken { 

  @PrimaryKey
  private Key token;

  public SessionToken(Key token) {
    this.token = token;
  }

  //getters and setters...
}

Then I want to create a user, so I do:

PersistenceManager pm = PMF.get().getPersistenceManager();

User user = new User();

Key key = KeyFactory.createKey(SessionToken.class.getSimpleName(), 
    "VALID_USER_TOKEN");
user.setSessionToken(new SessionToken(key));

try {
  pm.makePersistent(user);
} finally {
  pm.close();
}

Here is the full exception dump:

javax.jdo.JDOFatalUserException: Detected attempt to establish User(15) as the parent of SessionToken("VALID_USER_TOKEN") but the entity identified by SessionToken("VALID_USER_TOKEN") has already been persisted without a parent.  A parent cannot be established or changed once an object has been persisted.
    at org.datanucleus.api.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:498)
    at org.datanucleus.api.jdo.JDOPersistenceManager.jdoMakePersistent(JDOPersistenceManager.java:736)
    at org.datanucleus.api.jdo.JDOPersistenceManager.makePersistent(JDOPersistenceManager.java:756)
    at com.mockgaeapp.UsersEndpoint.register(UsersEndpoint.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:115)
    at com.google.api.server.spi.SystemService.invokeServiceMethod(SystemService.java:239)
    at com.google.api.server.spi.SystemServiceServlet.execute(SystemServiceServlet.java:161)
    at com.google.api.server.spi.SystemServiceServlet.doPost(SystemServiceServlet.java:120)
    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.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:123)
    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:61)
    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:125)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97)
    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:94)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:409)
    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)

I don't understand the error because I don't persist the SessionToken object before persisting the User...

What does this Exception mean?

Upvotes: 1

Views: 817

Answers (1)

Takashi Matsuo
Takashi Matsuo

Reputation: 3436

Your code tells JDO to have 'owned' relationship between the User and SessionToken objects, where the child entity is supposed to have parent's key as its parent (in the datastore terminology), while you explicitly set the key without the parent.

Please try using 'unowned' relationship.

Upvotes: 2

Related Questions