Entitätsbeziehungen in JDO

Beziehungen zwischen persistenten Objekten können mithilfe von Feldern der Objekttypen modelliert werden. Eine Beziehung zwischen persistenten Objekten kann als beansprucht ("owned") bezeichnet werden, wenn eines der Objekte nicht ohne das andere existieren kann, bzw. als unbeansprucht ("unwowned"), wenn beide Objekte unabhängig von ihrer Beziehung zueinander bestehen können. Mit der App Engine-Implementierung der JDO-Schnittstelle können sowohl beanspruchte als auch unbeanspruchte 1:1- und 1:n-Beziehungen modelliert werden. Dies schließt uni- und bidirektionale Beziehungen ein.

Unbeanspruchte Beziehungen werden von Version 1 des DataNucleus-Plug-ins für App Engine nicht unterstützt. Sie können diese Beziehungen jedoch selbst verwalten, indem Sie Datenspeicherschlüssel direkt in Feldern speichern. App Engine erstellt automatisch verwandte Entitäten in Entitätengruppen, um die gemeinsame Aktualisierung von verwandten Objekten zu unterstützen. Wann Datenspeichertransaktionen verwendet werden, wird allerdings in der App entschieden.

Version 2.x des DataNucleus-Plug-ins für App Engine unterstützt unbeanspruchte Beziehungen mit einer natürlichen Syntax. Im Abschnitt Unbeanspruchte Beziehungen wird erklärt, wie in den jeweiligen Plug-in-Versionen unbeanspruchte Beziehungen erstellt werden. Wie Sie ein Upgrade auf Version 2.x des DataNucleus-Plug-ins für App Engine durchführen, erfahren Sie unter Zu Version 2.x des DataNucleus-Plug-ins für App Engine migrieren.

Beanspruchte ("owned") 1-zu-1-Beziehungen

Eine unidirektionale, beanspruchte 1:1-Beziehung zwischen zwei persistenten Objekten wird durch Verwendung eines Felds erstellt, dessen Typ die Klasse der verwandten Klasse ist.

Das folgende Beispiel definiert die Datenklasse "ContactInfo" und die Datenklasse "Employee", mit einer 1:1-Beziehung zwischen Employee und ContactInfo.

ContactInfo.java

import com.google.appengine.api.datastore.Key;
// ... imports ...

@PersistenceCapable
public class ContactInfo {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String streetAddress;

    // ...
}

Employee.java

import ContactInfo;
// ... imports ...

@PersistenceCapable
public class Employee {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private ContactInfo contactInfo;

    ContactInfo getContactInfo() {
        return contactInfo;
    }
    void setContactInfo(ContactInfo contactInfo) {
        this.contactInfo = contactInfo;
    }

    // ...
}

Die persistenten Objekte werden als zwei separate Entitäten, mit zwei verschiedenen Typen, im Datenspeicher dargestellt. Die Beziehung wird durch eine Entitätsgruppenbeziehung dargestellt: Der Schlüssel des untergeordneten Elements verwendet den Schlüssel des übergeordneten Elements als übergeordnetes Element in der Entitätengruppe. Greift die Anwendung über das Feld des übergeordneten Objekts auf das untergeordnete Objekt zu, führt die JDO-Implementierung eine Abfrage nach dem übergeordneten Element in der Entitätengruppe durch, um das untergeordnete Element abzurufen.

Die untergeordnete Klasse muss ein Schlüsselfeld haben, dessen Typ die Informationen zum übergeordneten Schlüssel enthalten kann: entweder einen "Key" (Schlüssel), oder einen als String codierten Schlüsselwert. Weitere Informationen zu Schlüsselfeldtypen finden Sie unter Daten erstellen: Schlüssel.

Eine bidirektionale 1:1-Beziehung mit einer Annotation im Feld der untergeordneten Klasse erstellen Sie mithilfe von Feldern in beiden Klassen, die deklariert, dass die Felder eine bidirektionale Beziehung darstellen. Das Feld der untergeordneten Klasse muss eine @Persistent-Annotation mit dem Argument mappedBy = "..." aufweisen, wobei der Wert der Name des Felds in der übergeordneten Klasse ist. Wenn das Feld in einem Objekt mit Daten gefüllt wird, wird auch das zugehörige Referenzfeld im anderen Objekt automatisch mit Daten gefüllt.

ContactInfo.java

import Employee;

// ...
    @Persistent(mappedBy = "contactInfo")
    private Employee employee;

Untergeordnete Objekte werden aus dem Datenspeicher geladen, wenn zum ersten Mal darauf zugegriffen wird. Wenn Sie nicht auf das untergeordnete Objekt in einem übergeordneten Objekt zugreifen, wird die Entität für das untergeordnete Objekt nie geladen. Sie haben zwei Möglichkeiten, das untergeordnete Objekt zu laden: Sie können den Zeitstempel aktualisieren, bevor Sie den PersistenceManager schließen. Im Beispiel oben rufen Sie hierzu getContactInfo() auf. Alternativ fügen Sie der standardmäßigen Abrufgruppe das untergeordnete Feld explizit hinzu, damit es mit dem übergeordneten Objekt abgerufen und geladen wird:

Employee.java

import ContactInfo;

// ...
    @Persistent(defaultFetchGroup = "true")
    private ContactInfo contactInfo;

Beanspruchte ("owned") 1-zu-N-Beziehungen

Zur Erstellung einer 1:n-Beziehung zwischen Objekten einer Klasse und mehreren Objekten einer anderen Klasse verwenden Sie eine Sammlung der verwandten Klasse:

Employee.java

import java.util.List;

// ...
    @Persistent
    private List<ContactInfo> contactInfoSets;

Bidirektionale 1-zu-N-Beziehungen ähneln 1-zu-1-Beziehungen, mit einem Feld in der übergeordneten Klasse mittels der Annotation @Persistent(mappedBy = "..."), wobei der Wert der Name des Felds in der untergeordneten Klasse ist:

Employee.java

import java.util.List;

// ...
    @Persistent(mappedBy = "employee")
    private List<ContactInfo> contactInfoSets;

ContactInfo.java

import Employee;

// ...
    @Persistent
    private Employee employee;

Die unter Datenklassen definieren: Sammlungen aufgeführten Sammlungstypen werden für 1:n-Beziehungen unterstützt. Allerdings werden für 1:n-Beziehungen keine Arrays unterstützt.

App Engine unterstützt keine Join-Abfragen: Es ist nicht möglich, übergeordnete Entitäten mithilfe von Attributen einer untergeordneten Entität abzufragen. Attribute von eingebetteten Klasse können abgefragt werden, da bei eingebetteten Klassen Attribute in der übergeordneten Entität gespeichert werden. Weitere Informationen erhalten Sie unter Datenklassen definieren: Eingebettete Klassen.

Beibehalten der Reihenfolge bei geordneten Sammlungen

Bei geordneten Sammlungen, wie z. B. List<...>, wird die Reihenfolge der Objekte beim Speichern des übergeordneten Objekts beibehalten. Bei JDO ist es erforderlich, dass die Datenbanken diese Reihenfolge beibehalten, indem die Position der einzelnen Objekte als Property des Objekts gespeichert wird. App Engine speichert dies als Eigenschaft der zugehörigen Entität und verwendet dafür als Eigenschaftsnamen den Namen des übergeordneten Felds, gefolgt von _INTEGER_IDX. Positionseigenschaften sind ineffizient. Wird ein Element in der Sammlung hinzugefügt, entfernt oder verschoben, müssen alle Entitäten, die nach der geänderten Stelle in der Sammlung kommen, aktualisiert werden. Dieser Vorgang kann langsam und fehleranfällig sein, wenn er nicht in einer Transaktion durchgeführt wird.

Wenn Sie keine bestimmte Reihenfolge in der Sammlung beibehalten müssen, jedoch die Verwendung eines geordneten Sammlungstyps erforderlich ist, können Sie eine Sortierung angeben, die auf den Properties der Elemente beruht, indem Sie eine Annotation verwenden, eine von DataNucleus bereitgestellte Erweiterung zu JDO:

import java.util.List;
import javax.jdo.annotations.Extension;
import javax.jdo.annotations.Order;
import javax.jdo.annotations.Persistent;

// ...
    @Persistent
    @Order(extensions = @Extension(vendorName="datanucleus",key="list-ordering", value="state asc, city asc"))
    private List<ContactInfo> contactInfoSets = new ArrayList<ContactInfo>();

Die Annotation @Order (unter Verwendung der Erweiterung list-ordering) gibt die gewünschte Reihenfolge der Elemente der Sammlung als JDOQL-Sortierklausel an. Für die Sortierung werden Werte von Properties der Elemente verwendet. Wie bei Abfragen müssen alle Elemente einer Sammlung Werte für die in der Sortierklausel verwendeten Properties haben.

Durch den Zugriff auf eine Sammlung wird eine Abfrage durchgeführt. Verwendet die Sortierklausel eines Feldes mehr als eine Sortierreihenfolge, ist für die Abfrage ein Datastore-Index erforderlich. Weitere Informationen erhalten Sie auf der Seite Datenspeicherindexe.

Aus Effizienzgründen sollten Sie möglichst immer eine explizite Sortierklausel für 1:n-Beziehungen sortierter Sammlungstypen verwenden.

Unbeanspruchte ("unowned") Beziehungen

Neben beanspruchten Beziehungen bietet die JDO-API auch die Möglichkeit zur Verwaltung unbeanspruchter Beziehungen. Diese Option funktioniert je nach verwendeter Version des DataNucleus-Plug-ins für App Engine anders:

  • In Version 1 des DataNucleus-Plug-ins werden keine unbeanspruchten Beziehungen mit einer natürlichen Syntax implementiert. Sie haben jedoch die Möglichkeit, diese Beziehungen mit Key-Werten anstelle von Instanzen (oder Sammlungen von Instanzen) Ihrer Modellobjekte zu verwalten. Stellen Sie sich das Speichern von Key-Objekten wie das Modellieren eines beliebigen Fremdschlüssels zwischen zwei Objekten vor. Der Datenspeicher übernimmt keine Garantie für die referenzielle Integrität bei diesen Schlüsselreferenzen, doch durch die Verwendung von Key ist es sehr einfach, eine beliebige Beziehung zwischen zwei Objekten zu modellieren und anschließend abzurufen.

    Wenn Sie sich für diesen Weg entscheiden, müssen Sie allerdings darauf achten, die geeigneten Schlüssel auszuwählen. JDO und der Compiler prüfen keine Key-Typen.
  • In Version 2.x des DataNucleus-Plug-ins werden unbeanspruchte Beziehungen mit einer natürlichen Syntax implementiert.

Tipp: In einigen Fällen kann es erforderlich sein, eine beanspruchte Beziehung so zu modellieren, als sei sie unbeansprucht. Dies liegt daran, dass alle Objekte, die an einer beanspruchten Beziehung beteiligt sind, automatisch in derselben Entitätengruppe abgelegt werden und eine Entitätengruppe nur ein bis zehn Schreibvorgänge pro Sekunde unterstützen kann. Empfängt ein übergeordnetes Objekt beispielsweise 0,75 Schreibvorgänge pro Sekunde und ein untergeordnetes Objekt ebenfalls 0,75 Schreibvorgänge pro Sekunde, dann ist es sinnvoll, diese Beziehung als nicht beansprucht zu modellieren, sodass sich das übergeordnete und das untergeordnete Element jeweils in einer eigenen, unabhängigen Entitätengruppe befinden können.

Unbeanspruchte ("unowned") 1-zu-1-Beziehungen

Angenommen, Sie möchten Person und Speise ("Food") modellieren, wobei für eine Person nur eine einzige Lieblingsspeise gelten kann, eine Lieblingsspeise jedoch nicht zu der Person gehört, da sie die Lieblingsspeise einer beliebigen Anzahl von Personen sein kann. Der folgende Abschnitt zeigt, wie das geht.

In JDO 2.3

In diesem Beispiel ordnen wir Person einem Element des Typs Key zu, wobei der Key die eindeutige Kennung eines Objekts Food ist. Wenn sich die Instanz von Person und die Instanz von Food, auf die Person.favoriteFood verweist, nicht in derselben Entitätengruppe befinden, können Sie die Person und die Lieblingsspeise dieser Person nur in einer einzelnen Transaktion aktualisieren, wenn in der JDO-Konfiguration XG-Transaktionen (gruppenübergreifende Transaktionen) aktiviert sind.

Person.java

// ... imports ...

@PersistenceCapable
public class Person {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private Key favoriteFood;

    // ...
}

Food.java

import Person;
// ... imports ...

@PersistenceCapable
public class Food {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    // ...
}

In JDO 3.0

In diesem Beispiel ordnen wir Person keinen Schlüssel für die Lieblingsspeise zu, sondern erstellen ein privates Element des Typs Food:

Person.java

// ... imports ...

@PersistenceCapable
public class Person {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    @Unowned
    private Food favoriteFood;

    // ...
}

Food.java

import Person;
// ... imports ...

@PersistenceCapable
public class Food {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    // ...
}

Unbeanspruchte ("unowned") 1-zu-N-Beziehungen

Angenommen wir möchten zulassen, dass eine Person mehrere Lieblingsspeisen haben kann. Auch hier gehört wieder eine Lieblingsspeise nicht zu der Person, da sie die Lieblingsspeise einer beliebigen Anzahl von Personen sein kann:

In JDO 2.3

In diesem Beispiel geben wir "Person" nicht ein Element des Typs Set<Food> zur Darstellung der Lieblingsspeise der Person, sondern wir geben "Person" ein Element des Typs Set<Key>, wobei das Set die eindeutigen Kennungen der Food-Objekte enthält. Wenn eine Instanz von Person und eine Instanz von Food in Person.favoriteFoods enthalten sind, diese aber nicht zur selben Entitätengruppe gehören, müssen Sie in der JDO-Konfiguration XG-Transaktionen (gruppenübergreifende Transaktionen) aktivieren. Anschließend können Sie sie in einer einzelnen Transaktion aktualisieren.

Person.java

// ... imports ...

@PersistenceCapable
public class Person {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private Set<Key> favoriteFoods;

    // ...
}

In JDO 3.0

In diesem Beispiel ordnen wir "Person" einem Element des Typs Set<Food> zu, bei dem das Set die Lieblingsspeisen der Person darstellt.

Person.java

// ... imports ...

@PersistenceCapable
public class Person {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private Set<Food> favoriteFoods;

    // ...
}

N-zu-N-Beziehungen

Wir können eine n:n-Beziehung modellieren, indem wir Sammlungen von Schlüsseln auf beiden Seiten der Beziehung unterhalten. Passen wir einfach unser Beispiel so an, dass bei Food die Personen aufgezeichnet werden können, die dieses Element als Lieblingsspeise ("favorite") betrachten:

Person.java

import java.util.Set;
import com.google.appengine.api.datastore.Key;

// ...
    @Persistent
    private Set<Key> favoriteFoods;

Food.java

import java.util.Set;
import com.google.appengine.api.datastore.Key;

// ...
    @Persistent
    private Set<Key> foodFans;

In diesem Beispiel unterhält Person ein Set von Key-Werten, die die Food-Objekte, bei denen es sich um Lieblingsspeisen handelt, eindeutig kennzeichnen, und Food unterhält ein Set von Key-Werten, die die Person-Objekte, die die entsprechende Speise als Lieblingsspeise betrachten, eindeutig kennzeichnen.

Bei der Modellierung einer N-zu-N-Beziehung mithilfe von Key-Werten, müssen Sie beachten, dass die App dafür zuständig ist, die beiden Seiten der Beziehung zu pflegen:

Album.java


// ...
public void addFavoriteFood(Food food) {
    favoriteFoods.add(food.getKey());
    food.getFoodFans().add(getKey());
}

public void removeFavoriteFood(Food food) {
    favoriteFoods.remove(food.getKey());
    food.getFoodFans().remove(getKey());
}

Wenn eine Instanz von Person und eine Instanz von Food in Person.favoriteFoods enthalten sind, diese aber nicht zur selben Entitätengruppe gehören, müssen Sie in Ihrer JDO-Konfiguration XG-Transaktionen (gruppenübergreifende Transaktionen) aktivieren. Anschließend können Sie sie in einer einzelnen Transaktion aktualisieren.

Beziehungen, Entitätengruppen und Transaktionen

Speichert Ihre Anwendung ein Objekt mit beanspruchten ("owned") Beziehungen im Datenspeicher, werden alle anderen über Beziehungen erreichbaren Objekte automatisch gespeichert, die gespeichert werden müssen, weil sie neu sind oder seit dem letzten Laden geändert wurden. Dieses Verhalten hat wichtige Folgen für Transaktionen und Entitätengruppen.

Betrachten Sie das folgende Beispiel, in dem eine unidirektionale Beziehung zwischen den obigen Klassen Employee und ContactInfo verwendet wird:

    Employee e = new Employee();
    ContactInfo ci = new ContactInfo();
    e.setContactInfo(ci);

    pm.makePersistent(e);

Wenn das neue Employee-Objekt mit der Methode pm.makePersistent() gespeichert wird, wird das neue verwandte ContactInfo-Objekt automatisch gespeichert. Da beide Objekte neu sind, erstellt App Engine zwei Entitäten in derselben Entitätsgruppe, wobei die Employee-Entität als übergeordnetes Element der ContactInfo-Entität verwendet wird. Umgekehrt gilt: Wenn das Employee-Objekt bereits gespeichert wurde und das zugehörige ContactInfo-Objekt neu ist, erstellt App Engine die ContactInfo-Entität mit der bestehenden Employee-Entität als übergeordnetem Element.

Beachten Sie jedoch, dass für den Aufruf von pm.makePersistent() in diesem Beispiel keine Transaktion verwendet wird. Ohne explizite Transaktion werden beide Entitäten in separaten atomischen Aktionen erstellt. In diesem Fall ist es möglich, dass die Erstellung der Entität Employee erfolgreich ist, die Erstellung der Entität ContactInfo dagegen fehlschlägt. Um sicherzustellen, dass entweder beide Entitäten erfolgreich erstellt werden oder keine davon, müssen Sie eine Transaktion verwenden:

    Employee e = new Employee();
    ContactInfo ci = new ContactInfo();
    e.setContactInfo(ci);

    try {
        Transaction tx = pm.currentTransaction();
        tx.begin();
        pm.makePersistent(e);
        tx.commit();
    } finally {
        if (tx.isActive()) {
            tx.rollback();
        }
    }

Wurden beide Objekte gespeichert, bevor die Beziehung erstellt wurde, kann App Engine die vorhandene Entität ContactInfo nicht in die Entitätengruppe der Entität Employee verschieben, da Entitätengruppen nur bei der Erstellung von Entitäten zugewiesen werden können. App Engine kann die Beziehung mit einer Referenz herstellen, die verwandten Entitäten befinden sich dann jedoch nicht in derselben Gruppe. In diesem Fall können beide Entitäten in derselben Transaktion aktualisiert oder gelöscht werden, vorausgesetzt, Sie haben in Ihrer JDO-Konfiguration gruppenübergreifende (XG) Transaktionen aktiviert. Falls Sie keine XG-Transaktionen verwenden, wird durch den Versuch, Entitäten unterschiedlicher Gruppen mit derselben Transaktion zu aktualisieren oder zu löschen, die Ausnahme JDOFatalUserException ausgelöst.

Beim Speichern eines übergeordneten Objekts, dessen untergeordnete Objekte geändert wurden, werden auch die Änderungen an den untergeordneten Objekten gespeichert. Es empfiehlt sich zuzulassen, dass übergeordnete Objekte die Persistenz für alle zugehörigen untergeordneten Objekte auf diese Weise aufrechterhalten und beim Speichern von Änderungen Transaktionen verwenden.

Abhängige untergeordnete Elemente und Löschweitergaben

Eine beanspruchte Beziehung kann "abhängig" sein, das heißt, für ein untergeordnetes Objekt ist immer ein übergeordnetes Objekt erforderlich. Ist eine Beziehung abhängig und ein übergeordnetes Objekt wird gelöscht, werden alle untergeordneten Objekte ebenfalls gelöscht. Wenn eine beanspruchte, abhängige Beziehung aufgebrochen wird, indem dem abhängigen Feld im übergeordneten Element ein neuer Wert zugewiesen wird, so wird auch das alte untergeordnete Element gelöscht. Sie können eine beanspruchte 1-zu-1-Beziehung als abhängig deklarieren, indem Sie zur Persistent-Annotation des Felds dependent="true" im übergeordneten Objekt hinzufügen, das auf das untergeordnete Element verweist:

// ...
    @Persistent(dependent = "true")
    private ContactInfo contactInfo;

Sie können eine beanspruchte 1:n-Beziehung als abhängig deklarieren, indem Sie eine @Element(dependent = "true")-Annotation zu dem Feld im übergeordneten Objekt hinzufügen, das auf die Sammlung der untergeordneten Elemente verweist:

import javax.jdo.annotations.Element;
// ...
    @Persistent
    @Element(dependent = "true")
    private List contactInfos;

Wie bei der Erstellung und Aktualisierung von Objekten gilt auch hier: Wenn es erforderlich ist, dass jeder Löschvorgang bei einer Löschweitergabe in einer einzelnen atomischen Aktion erfolgt, so müssen Sie den Löschvorgang in einer Transaktion durchführen.

Hinweis: Das Löschen der abhängigen untergeordneten Objekte wird von der JDO-Implementierung übernommen, nicht vom Datenspeicher. Wenn Sie eine übergeordnete Entität mit der Low-Level-API oder der Google Cloud Console löschen, werden die untergeordneten Objekte nicht gelöscht.

Polymorphe Beziehungen

Obwohl die JDO-Spezifikation die Unterstützung für polymorphe Beziehungen umfasst, werden polymorphe Beziehungen noch nicht in der App Engine JDO-Implementierung unterstützt. Wir hoffen, diese Einschränkung in späteren Versionen des Produkts beseitigen zu können. Müssen Sie auf mehrere Typen von Objekten über eine gemeinsame Basisklasse verweisen, empfehlen wir, dieselbe Strategie zu verwenden wie für die Implementierung unbeanspruchter ("unowned") Beziehungen: Speichern einer Schlüsselreferenz. Beispiel: Wenn Sie die Basisklasse Recipe mit den Spezialisierungen Appetizer, Entree und Dessert verwenden und das Lieblingsrezept Recipe eines Kochs (Chef) modellieren möchten, können Sie dazu wie folgt vorgehen:

Recipe.java

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Inheritance;
import javax.jdo.annotations.InheritanceStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable
@Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class Recipe {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private int prepTime;
}

Appetizer.java

// ... imports ...

@PersistenceCapable
public class Appetizer extends Recipe {
// ... appetizer-specific fields
}

Entree.java

// ... imports ...

@PersistenceCapable
public class Entree extends Recipe {
// ... entree-specific fields
}

Dessert.java

// ... imports ...

@PersistenceCapable
public class Dessert extends Recipe {
// ... dessert-specific fields
}

Chef.java

// ... imports ...

@PersistenceCapable
public class Chef {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent(dependent = "true")
    private Recipe favoriteRecipe;
}

Leider erhalten Sie, wenn Sie eine Entree instanziieren und sie einem Chef.favoriteRecipe zuweisen, eine UnsupportedOperationException, wenn Sie versuchen, das Chef-Objekt zu persistieren. Dies liegt daran, dass der Laufzeittyp des Objekts, Entree, nicht mit dem deklarierten Typ des Beziehungsfelds, Recipe, übereinstimmt. Das Problem lässt sich dadurch umgehen, dass der Typ von Chef.favoriteRecipevon Recipe in Key geändert wird:

Chef.java

// ... imports ...

@PersistenceCapable
public class Chef {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private Key favoriteRecipe;
}

Da Chef.favoriteRecipe kein Beziehungsfeld mehr ist, kann es auf ein Objekt mit beliebigem Typ verweisen. Der Nachteil besteht darin, dass Sie diese Beziehung wie bei einer unbeanspruchten Beziehung manuell verwalten müssen.