Search code examples
javagoogle-app-enginegoogle-cloud-datastorejdodatanucleus

PersistenceManager.getObjectById with ancestor key


I'm creating a Java GAE app using JDO for the Datastore.

I have these 2 classes User and Folder.

@PersistenceCapable
public class User {    
  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Key key;
  @Persistent
  private Folder rootFolder;    
  //getters and setters...
}

@PersistenceCapable
public class Folder {   
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Key key;
  @Persistent
  private List<Folder> subfolders;    
  //getters and setters...
}

I want to retrieve a Folder given its ID (Long), so I tried to do this:

Key key = KeyFactory.createKey(Folder.class.getSimpleName(), 371);
folder = this.pm.getObjectById(Folder.class, key);

But this doesn't work. It throws this exception:

org.datanucleus.exceptions.NucleusObjectNotFoundException: 
Could not retrieve entity of kind Folder with key Folder(371)

I think this is because the Key of the folder contains its ancestors' keys as well, but my problem is that I can't know all the ancestors path of a Folder given its ID.

What I can know it's the first ancestor in the path, namely the User that owns the root folder. I found there's a Query.setAncestor(Key) method, which may be suitable here, but it's only for a Query, not for a "Key lookup"...

Anybody knows how to do this?


Solution

  • I'm not familiar with the Java side of things, but the datastore is essentially the same on both Java/Python.

    Once you start using ancestors, ids/names are no longer unique identifiers for entities. The unique identifier is the combination of the id plus the entire ancestor path. It's possible to have multiple entities with the same id under different ancestors. Without the entire ancestor path, you don't have the unique key.

    The best you can do is to do a query for entities that match that id, and then figure out which one you want within within the returned set. You can do a getObjectById again after the Query to get the consistent version, though that doesn't really help you for new entities.

    You might want to revisit some things like whether or not you really need ancestors in this situation. If you do, you would need to design some place to store the entire ancestor path of the folder, so that you might refer to that when you do your getObjectById. Or if you plan for folder names to be unique, use a name instead of an id so that it may be easier to lookup. etc.