Daten in JDO erstellen, abrufen und löschen

Das Speichern eines JDO-Datenobjekts im Datenspeicher erfolgt einfach durch Aufrufen der makePersistent()-Methode der PersistenceManager-Instanz. Die App Engine JDO-Implementierung verwendet das Primärschlüsselfeld des Objekts, um zu verfolgen, welche Datenspeicherentität dem Datenobjekt entspricht, und sie kann automatisch Schlüssel für neue Objekte generieren. Sie können Schlüssel zum raschen Abrufen von Entitäten verwenden und Sie können Schlüssel aus bekannten Werten (z. B. Konto-IDs) erstellen.

Objekte persistent machen

Rufen Sie die Methode makePersistent() des PersistenceManagers auf und übergeben Sie sie an die Instanz, um ein einfaches Datenobjekt im Datenspeicher zu speichern.

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

Employee e = new Employee("Alfred", "Smith", new Date());

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

Der Aufruf an makePersistent() ist synchron und kehrt erst zurück, wenn das Objekt gespeichert wurde und die Indizes aktualisiert wurden.

Zur Speicherung mehrerer Objekte in JDO rufen Sie die Methode makePersistentAll(...) mit einer Objektsammlung auf. Diese Methode verwendet einen einzelnen untergeordneten Batch-Speichervorgang, der effizienter ist als eine Reihe einzelner Aufrufe von makePersistent(...).

Hinweis: Wenn eines oder mehrere der persistenten Felder des Datenobjekts Referenzen auf andere persistente Datenobjekte sind und einige dieser Objekte noch nie gespeichert oder seit dem Laden geändert wurden, werden die referenzierten Objekte ebenfalls im Datenspeicher gespeichert. Siehe Beziehungen.

Schlüssel

Jede Entität besitzt einen Schlüssel, der für alle Entitäten in App Engine eindeutig ist. Ein vollständiger Schlüssel enthält mehrere Informationen, darunter die Anwendungs-ID, die Art und eine Entitäts-ID. (Schlüssel enthalten auch Informationen über Entitätengruppen; weitere Informationen finden Sie unter Transaktionen.)

Der Schlüssel eines Objekts wird in einem Feld in der Instanz gespeichert. Mit der Annotation @PrimaryKey können Sie das Primärschlüsselfeld erkennen.

Die Anwendung kann den ID-Teil des Schlüssels als String bereitstellen, wenn das Objekt erstellt wird, oder sie kann zulassen, dass der Datenspeicher automatisch eine numerische ID erstellt. Der vollständige Schlüssel muss für alle Entitäten im Datenspeicher eindeutig sein. Anders ausgedrückt: Ein Objekt muss eine ID aufweisen, die für alle Objekte derselben Art und mit demselben übergeordneten Element in der Entitätengruppe (sofern vorhanden) eindeutig ist. Sie wählen mithilfe des Feld- und Annotationstyps das gewünschte Verhalten aus.

Wenn die Klasse in einer Beziehung als "untergeordnete" Klasse verwendet wird, muss das Schlüsselfeld von einem Typ sein, der eine übergeordnete Entitätengruppe darstellen kann: entweder eine Schlüsselinstanz oder ein als String codierter Schlüsselwert. Weitere Informationen zu Entitätengruppen finden Sie unter Transaktionen und weitere Informationen zu Beziehungen finden Sie unter Beziehungen.

Tipp: Wenn die Anwendung ein neues Objekt erstellt und ihm die gleiche String-ID wie einem anderen Objekt zuweist, welches von der gleichen Art ist und das gleiche übergeordnete Element in der Entitätengruppe hat, wird dieses andere Objekt durch das Speichern des neuen Objekts im Datenspeicher überschrieben. Wenn Sie vor dem Erstellen eines neuen Objekts feststellen möchten, ob eine String-ID bereits verwendet wird, können Sie mithilfe einer Transaktion versuchen, eine Entität mit einer bestimmten ID abzurufen und sie dann erstellen, wenn sie noch nicht vorhanden ist. Siehe Transaktionen.

Es gibt vier Typen von Primärschlüsselfeldern:

Long

Eine ganze Zahl vom Typ "long" (java.lang.Long), eine automatisch vom Datenspeicher erstellte Entitäts-ID. Verwenden Sie dies für Objekte ohne übergeordnete Elemente in der Entitätengruppe, deren IDs automatisch vom Datenspeicher generiert werden sollen. Das Schlüsselfeld vom Typ "Long" einer Instanz wird beim Speichern der Instanz mit Daten gefüllt.

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
Nicht codierter String

Ein String (java.lang.String) und eine Entitäts-ID ("Schlüsselname"), die von der Anwendung beim Erstellen des Objekts bereitgestellt wird. Verwenden Sie dies für Objekte ohne übergeordnete Elemente in der Entitätengruppe, deren IDs automatisch von der Anwendung bereitgestellt werden sollen. Die Anwendung setzt dieses Feld vor dem Speichern auf die gewünschte ID.

import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    private String name;
Schlüssel

Eine Schlüsselinstanz (com.google.appengine.api.datastore.Key). Der Schlüsselwert enthält den Schlüssel des übergeordneten Elements in der Entitätengruppe (sofern vorhanden) sowie entweder die von der Anwendung zugewiesene String-ID oder die vom System erstellte numerische ID. Zum Erzeugen des Objekts mit einer von der Anwendung zugewiesenen String-ID erstellen Sie den Schlüsselwert mit der ID und setzen Sie das Feld auf den entsprechenden Wert. Zum Erzeugen des Objekts mit der vom System erstellten numerischen ID behalten Sie den NULL-Wert des Schlüsselfelds bei. (Informationen zur Verwendung von übergeordneten Elementen in der Entitätengruppe finden Sie unter Transaktionen.)

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;
    }

Die Anwendung kann mithilfe der KeyFactory-Klasse eine Schlüsselinstanz erstellen:

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);
Schlüssel als codierter String

Ähnlich wie Schlüssel, aber der Wert ist die codierte Stringform des Schlüssels. Mit codierten Stringschlüsseln können Sie Ihre Anwendung in portierbarer Form schreiben und dennoch die Entitätengruppen des App Engine Datastore nutzen.

import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

Die Anwendung kann diesen Wert vor dem Speichern mithilfe eines Schlüssels mit einem Namen füllen oder sie kann ihn bei NULL belassen. Wenn das codierte Schlüsselfeld den NULL-Wert aufweist, wird das Feld beim Speichern des Objekts mit einem vom System generierten Schlüssel gefüllt.

Schlüsselinstanzen können mithilfe der KeyFactory-Methoden keyToString() und stringToKey() in die codierte Stringdarstellung und aus der codierten Stringdarstellung konvertiert werden.

Bei der Verwendung codierter Schlüsselstrings können Sie mit einem zusätzlichen Feld Zugriff auf den String bzw. die numerische ID eines Objekts bereitstellen:

    @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;

Ein "gae.pk-name"-Feld kann vor dem Speichern des Objekts auf einen Schlüsselnamen gesetzt werden. Beim Speichern des Objekts wird das codierte Schlüsselfeld mit dem vollständigen Schlüssel gefüllt, der den Schlüsselnamen enthält. Der Typ muss String sein.

Ein "gae.pk-id"-Feld wird ausgefüllt, wenn das Objekt gespeichert wird und nicht geändert werden kann. Der Typ muss "Long" sein.

Wenn ein neues Objekt mit einem generierten Schlüssel (ein Schlüsselfeld mit valueStrategy = IdGeneratorStrategy.IDENTITY) erstellt wird, ist der Schlüsselwert zuerst null. Das Schlüsselfeld wird mit Daten gefüllt, wenn das Objekt in den Datenspeicher geschrieben wird. Wenn Sie eine Transaktion verwenden, wird das Objekt geschrieben, wenn die Transaktion in einem Commit-Vorgang bestätigt wird. Andernfalls wird das Objekt beim Aufrufen der Methode makePersistent() geschrieben, sofern das Objekt erstellt wird, bzw. beim Aufrufen der Methode close() der PersistenceManager-Instanz, falls das Objekt aktualisiert wird.

Weitere Informationen zum Erstellen von Schlüsseln finden Sie unter Entitäten, Eigenschaften und Schlüssel.

Objekte nach Schlüssel abrufen

Mit der getObjectById()-Methode des PersistenceManagers können Sie ein Objekt mit seinem Schlüssel abrufen. Bei dieser Methode werden die Klasse für das Objekt sowie der folgende Schlüssel verwendet:

        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = pm.getObjectById(Employee.class, k);

Wenn die Klasse ein Schlüsselfeld verwendet, bei dem es sich um eine uncodierte String-ID (String) oder eine numerische ID (Long), getObjectByID() handelt, kann den einfachen Wert als Schlüsselparameter verwenden:

        Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");

Als Schlüsselargument kommen alle unterstützten Schlüsselfeldtypen (String-ID, numerische ID, Schlüsselwert, codierter Schlüsselstring) infrage. Der Typ muss dabei nicht mit dem Schlüsselfeld in der Klasse identisch sein. App Engine muss den vollständigen Schlüssel aus dem Klassennamen und dem bereitgestellten Wert ableiten können. String-IDs und numerische IDs schließen sich gegenseitig aus. Bei einem Aufruf mithilfe einer numerischen ID wird somit niemals eine Entität mit einer String-ID zurückgegeben. Wenn ein Schlüsselwert oder codierter Schlüsselstring verwendet wird, muss der Schlüssel auf eine Entität verweisen, deren Typ von der Klasse dargestellt wird.

Objekte aktualisieren

Eine Möglichkeit zur Aktualisierung von Objekten mit JDO besteht darin, das Objekt abzurufen und es dann zu ändern, während der PersistenceManager, der das Objekt zurückgab, noch geöffnet ist. Die Änderungen werden beim Schließen des PersistenceManagers persistent. Beispiel:

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();
    }
}

Da die Instanz Employee vom PersistenceManager zurückgegeben wurde, kennt dieser alle Änderungen, die an persistenten Feldern bei Employee vorgenommen wurden, und aktualisiert den Datenspeicher automatisch mit diesen Änderungen, wenn PersistenceManager geschlossen wird. Der PersistenceManager kennt die Änderungen, weil die Instanz "Employee" an ihn "angehängt" ist.

Sie können ein Objekt ändern, nachdem der PersistenceManager geschlossen wurde, indem Sie die Klasse als "trennbar" festlegen. Fügen Sie dazu das Attribut detachable zur Annotation @PersistenceCapable hinzu:

import javax.jdo.annotations.PersistenceCapable;

@PersistenceCapable(detachable="true")
public class Employee {
    // ...
}

Nun können Sie die Felder des Objekts "Employee" lesen und schreiben, nachdem der PersistenceManager, der es geladen hat, geschlossen wurde. Im folgenden Beispiel wird gezeigt, wie ein getrenntes Objekt nützlich eingesetzt werden kann:

public Employee getEmployee(User user) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    Employee employee, detached = null;
    try {
        employee = pm.getObjectById(Employee.class,
            "Alfred.Smith@example.com");

        // If you're using transactions, you can call
        // pm.setDetachAllOnCommit(true) before committing to automatically
        // detach all objects without calls to detachCopy or detachCopyAll.
        detached = pm.detachCopy(employee);
    } finally {
        pm.close();
    }
    return detached;
}

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);
    }
}

Getrennte Objekte sind eine praktische Alternative zur Erstellung von Datentransferobjekten. Weitere Informationen zum Arbeiten mit getrennten Objekten finden Sie in der DataNucleus-Dokumentation.

Objekte löschen

Rufen Sie die Methode deletePersistent()des PersistenceManagers mit folgendem Objekt auf, um ein Objekt aus dem Datenspeicher zu löschen:

pm.deletePersistent(e);

Rufen Sie die Methode deletePersistentAll(...) mit einer Sammlung von Objekten auf, um mehrere Objekte in JDO zu löschen. Diese Methode verwendet einen einzelnen untergeordneten Batch-Speichervorgang, der effizienter ist als eine Reihe einzelner Aufrufe von deletePersistent(...).

Wenn ein Objekt Felder mit untergeordneten Objekten enthält, die ebenfalls persistent sind, werden auch die untergeordneten Objekte gelöscht. Weitere Informationen finden Sie unter Beziehungen.

Sie können die JDOQL-Funktion "Nach Abfrage löschen" verwenden, um alle Objekte zu löschen, die mit einer Abfrage übereinstimmen. Weitere Informationen finden Sie unter Entitäten nach Abfrage löschen.