AppEngine JDO example #1: retrieve an object by ID
Posted by David Chandler on October 30, 2009
This is the first of a series of posts that will demonstrate a variety of JDO mappings and queries with AppEngine.
For starters, let’s retrieve an object from the Datastore given its ID. Here’s the User object in our domain model:
package com.roa.client.domain; import java.io.Serializable; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class User implements Serializable { private static final long serialVersionUID = -1126191336687818754L; @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private String firstName; ... }
Now we want to retrieve a User object given the ID property obtained from getId(). Easy enough, you think, as the JDO PersistenceManager provides a method for this:
// Doesn't quite work User attachedUser = (User) getPM().getObjectById(userId);
But there’s a catch. Confusingly, the ID needed by getObjectById is a JDO ID, not the object property named “id” that we’ve annotated as an IDENTITY. You can get the JDO ID one of two ways. Whenever the object is attached, such as just after you’ve called makePersistent() on it, you can call persistenceManager.getObjectId(obj) and keep it in a safe place for later use. Alternatively, you can use a JDO helper method at any time to get the JDO ID from the object ID:
// Must obtain the JDO ID first Object idInstance = getPM().newObjectIdInstance(User.class, user.getId()); User attachedUser = (User) getPM().getObjectById(idInstance);
Fortunately, if you know the object’s class, there’s a 2-arg getObjectById that does take the object’s primary key property:
User attachedUser = getPM().getObjectById(User.class, u.getId());
Of course, you can always just run a normal query on the object’s ID property, but it’s a bit ugly because the simplest query syntax always returns a Collection and we assume here that there will be only one matching object.
Query q = getPM().newQuery(User.class, "id == :userId"); List<User> users = (List<User>) q.execute(u.getId()); User attachedUser = users.get(0);
Better to let the query language know that we expect a unique object:
Query q = getPM().newQuery(User.class, "id == :userId"); q.setUnique(true); User attachedUser = (User) q.execute(u.getId());
Finallly, there’s the low-level Datastore API:
Key key = KeyFactory.createKey(User.class.getSimpleName(), u.getId()); Entity entity = DatastoreServiceFactory.getDatastoreService().get(key);
timo said
Thanks for posting this useful example.
yoyo said
3Q very much!
Vimal said
Hello David,
I just landed your website through a search on good tutorial on getObjectById in JDO.
I am stuck in a GAE-Java web app I am developing to send mails to user of my app and then track them (mail view and web view via different URLs). Will be thankful for any help/guidance.
Is it possible that same code to fetch an object from two different location in an app can behave differently. In my app, Worker URL of a task can easily fetch CONTACT object using the eMailID (annotated with @PrimaryKey, type String in CONTACT class). But when I try to fetch the same object using eMailId from a JSP (meant to track web view of the mail), it throws error. The only difference is – in first case eMailId is taken as a list from CONTACT objects while in second it is taken from RECIPIENT objects (stored as String – not primary key).
I tried these options – Normal query on eMail field, getObjectById with String mailId as well as with Key (generated using KeyFactory) but nothing seems to be working.
Please guide.
Regards
Vimal
luis said
tengo una duda como se generan llaves compuesta es decir que en mi clase tenga dos atributos con llave primarias
Johnny Wu said
I found something really “interesting”…not sure if I did something wrong or it’s a feature that’s not covered here:
I have an entity with a primary key in Key type:
@PrimaryKey
@Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
//@Persistent
private Key objectID;
When I called getObjectByID(MyObject.class, object.getID()), the code jumps out of the try statement and lands in the final statement.
However, when I invoked the same call using the “Key” (or OID) value, it returns the actual object.
The “tricky” thing is I can’t invoke anything to obtain the “Key” value for the object….or can I?