Google Code offered in: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
Saving a JDO data object to the datastore is a simple matter of calling the makePersistent()
method of the PersistenceManager instance. The App Engine JDO implementation uses the primary key field of the object to keep track of which datastore entity corresponds with the data object, and can generate keys for new objects automatically. You can use keys to retrieve entities quickly, and you can construct keys from known values (such as account IDs).
To store a simple data object in the datastore, you call the PersistenceManager's makePersistent()
method, passing it the instance.
PersistenceManager pm = PMF.get().getPersistenceManager(); Employee e = new Employee("Alfred", "Smith", new Date()); try { pm.makePersistent(e); } finally { pm.close(); }
The call to makePersistent()
is synchronous, and doesn't return until the object is saved and indexes are updated.
To save multiple objects in JDO, call the makePersistentAll(...)
method with a Collection of objects. In the current release, this is implemented similarly to a series of calls to makePersistent()
. In a future release, this method will perform more efficient batch calls to the datastore.
Note: If any of the data object's persistent fields are references to other persistent data objects, and any of those objects have never been saved or have changed since they were loaded, the referenced objects will also be saved to the datastore. See Relationships.
Every entity has a key that is unique over all entities in App Engine. A complete key includes several pieces of information, including the application ID, the kind, and an entity ID. (Keys also contain information about entity groups; see Transactions for more information.)
An object's key is stored in a field on the instance. You identify the primary key field using the @PrimaryKey
annotation.
The app can provide the ID portion of the key as a string when the object is created, or it can allow the datastore to generate a numeric ID automatically. The complete key must be unique across all entities in the datastore. In other words, an object must have an ID that is unique across all objects of the same kind and with the same entity group parent (if any). You select the desired behavior of the key using the type of the field and annotations.
If the class is used as a "child" class in a relationship, the key field must be of a type capable of representing an entity group parent: either a Key instance, or a Key value encoded as a string. See Transactions for more information on entity groups, and Relationships for more information on relationships.
Tip: If the app creates a new object and gives it the same string ID as another object of the same kind (and the same entity group parent), saving the new object overwrites the other object in the datastore. To detect whether a string ID is already in use prior to creating a new object, you can use a transaction to attempt to get an entity with a given ID, then create one if it doesn't exist. See Transactions.
There are 4 types of primary key fields:
A long integer (java.lang.Long
), an entity ID automatically generated by the datastore. Use this for objects without entity group parents whose IDs should be generated automatically by the datastore. The long key field of an instance is populated when the instance is saved.
import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; // ... @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id;
A string (java.lang.String
), an entity ID ("key name") provided by the application when the object is created. Use this for objects without entity group parents whose IDs should be provided by the application. The application sets this field to the desired ID prior to saving.
import javax.jdo.annotations.PrimaryKey; // ... @PrimaryKey private String name;
A Key instance (com.google.appengine.api.datastore.Key
). The key value includes the key of the entity group parent (if any) and either the app-assigned string ID or the system-generated numeric ID. To create the object with an app-assigned string ID, you create the Key value with the ID and set the field to the value. To create the object with a system-assigned numeric ID, you leave the key field null. (For information on how to use entity group parents, see Transactions.)
import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.google.appengine.api.datastore.Key; // ... @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; public void setKey(Key key) { this.key = key; }
The app can create a Key instance using the KeyFactory class:
import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; // ... Key key = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com"); Employee e = new Employee(); e.setKey(key); pm.makePersistent(e);
Similar to Key, but the value is the encoded string form of the key. Encoded string keys allow you to write your application in a portable manner and still take advantage of App Engine datastore entity groups.
import javax.jdo.annotations.Extension; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; import com.google.appengine.api.datastore.Key; // ... @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") private String encodedKey;
The app can populate this value prior to saving using a key with a name, or it can leave it null. If the encoded key field is null, the field is populated with a system-generated key when the object is saved.
Key instances can be converted to and from the encoded string representation using the KeyFactory methods keyToString()
and stringToKey()
.
When using encoded key strings, you can provide access to an object's string or numeric ID with an additional field:
@PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") private String encodedKey; @Persistent @Extension(vendorName="datanucleus", key="gae.pk-name", value="true") private String keyName; // OR: @Persistent @Extension(vendorName="datanucleus", key="gae.pk-id", value="true") private Long keyId;
A "gae.pk-name"
field can be set to a key name prior to saving the object. When the object is saved, the encoded key field is populated with the complete key that includes the key name. Its type must be String.
A "gae.pk-id"
field is populated when the object is saved, and cannot be modified. Its type must be Long.
When a new object with a generated key (a key field using valueStrategy = IdGeneratorStrategy.IDENTITY
) is created, its key value starts out null
. The key field is populated when the object is written to the datastore. If you are using a transaction, the object is written when the transaction is committed. Otherwise, the object is written when the makePersistent()
method is called if the object is being created, or when the PersistenceManager instance's close()
method is called if the object is being updated.
If the application knows every element of an entity's complete key, the application can create the corresponding Key
object without the object.
For a key for an entity without an entity group parent, you can use the createKey()
static method of the KeyFactory
class. This method takes a kind (the simple name of the class) and either an app-assigned string ID or a system-assigned numeric ID, and returns a Key
object. For example, to recreate the key of an entity of kind "Employee"
with key name "Alfred.Smith@example.com"
(and no entity group parent):
import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; // ... Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
To recreate a key of an entity of kind "Employee"
with a system-assigned numeric ID of 52234
(and no entity group parent):
Key k = KeyFactory.createKey(Employee.class.getSimpleName(), 52234);
Keys can be converted to and from a string representation using the KeyFactory class's keyToString()
and stringToKey()
methods, respectively. (Note that this is different from the Key class's toString()
method, which returns a human-readable value suitable for debugging.)
For a key for an entity with an entity group parent, you can use the KeyFactory.Builder class:
import com.google.appengine.api.datastore.Key; import com.google.appengine.api.datastore.KeyFactory; // ... Key k = new KeyFactory.Builder(Employee.class.getSimpleName(), 52234).addChild(ExpenseReport.class.getSimpleName(), "A23Z79").getKey();
The Builder instance's addChild()
method returns the Builder, so you can chain calls to add each element of the key path. To get the complete Key value for a given builder, you call the Builder's getKey()
method.
For more information about entity groups, see Transactions.
To retrieve an object given its key, use the PersistenceManager's getObjectById()
method. The method takes the class for the object, and key:
Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com"); Employee e = pm.getObjectById(Employee.class, k);
If the class uses a key field that is an unencoded string ID (String
) or numeric ID (Long
), getObjectByID()
can take the simple value as the key parameter:
Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");
The key argument can be of any of the supported key field types (string ID, numeric ID, Key value, encoded key string), and can be of a different type than the key field in the class. App Engine must be able to derive the complete key from the class name and the provided value. String IDs and numeric IDs are exclusive, so a call using a numeric ID will never return an entity with a string ID. If a Key value or encoded key string is used, the key must refer to an entity whose kind is represented by the class.
One way to update an object with JDO is to fetch the object, then modify it while the PersistenceManager that returned the object is still open. Changes are persisted when the PersistenceManager is closed. For example:
public void updateEmployeeTitle(User user, String newTitle) { PersistenceManager pm = PMF.get().getPersistenceManager(); try { Employee e = pm.getObjectById(Employee.class, user.getEmail()); if (titleChangeIsAuthorized(e, newTitle) { e.setTitle(newTitle); } else { throw new UnauthorizedTitleChangeException(e, newTitle); } } finally { pm.close(); } }
Since the Employee
instance was returned by the PersistenceManager, the PersistenceManager knows about any modifications that are made to Persistent fields on the Employee
and automatically updates the datastore with these modifications when the PersistenceManager is closed. It know this because the Employee instance is "attached" to the PersistenceManager.
You can modify an object after the PersistenceManager has been closed by declaring the class as "detachable." To do this, add the detachable
attribute to the @PersistenceCapable
annotation:
import javax.jdo.annotations.IdentityType; import javax.jdo.annotations.PersistenceCapable; @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true") public class Employee { // ... }
Now you can read and write the fields of an Employee object after the PersistenceManager that loaded it has been closed. The following example illustrates how a detached object might be useful:
public Employee getEmployee(User user) { PersistenceManager pm = PMF.get().getPersistenceManager(); pm.setDetachAllOnCommit(true); try { e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com"); } finally { pm.close(); } return e; } public void updateEmployeeTitle(Employee e, String newTitle) { if (titleChangeIsAuthorized(e, newTitle) { e.setTitle(newTitle); PersistenceManager pm = PMF.get().getPersistenceManager(); try { pm.makePersistent(e); } finally { pm.close(); } } else { throw new UnauthorizedTitleChangeException(e, newTitle); } }
Detached objects are a nice alternative to creating data transfer objects. For more information on working with detached objects please see the DataNucleus documentation.
To delete an object from the datastore, call the PersistenceManager's deletePersistent()
method with the object:
pm.deletePersistent(e);
If an object has fields containing child objects that are also persistent, the child objects are also deleted. See Relationships for more information.