Utiliser JPA avec App Engine

Java Persistence API (JPA) est une interface standard permettant d'accéder à des bases de données en Java. Elle fournit un mappage automatique entre les classes Java et les tables de base de données. Un plug-in Open Source est disponible pour utiliser JPA avec Datastore. Cette page explique comment faire vos premiers pas avec ce plug-in.

Avertissement : Nous pensons que la plupart des développeurs pourront améliorer leur expérience en tirant parti de l'API Datastore de bas niveau ou de l'une des API Open Source développées spécifiquement pour Datastore, comme Objectify. JPA a été conçue pour être utilisée avec des bases de données classiques et n'a donc aucun moyen de représenter explicitement certains des aspects qui différencient Datastore des bases de données relationnelles, tels que les groupes d'entités et les requêtes ascendantes. Cela peut entraîner des problèmes subtils, difficiles à comprendre et à résoudre.

Le SDK Java App Engine intègre la version 2.x du plug-in DataNucleus pour Datastore. Ce plugin correspond à la version 3.0 de la plate-forme d'accès DataNucleus, ce qui vous permet d'utiliser App Engine Datastore via JPA 2.0.

Pour plus d'informations sur JPA, consultez la documentation d'Access Platform 3.0, en particulier les sections concernant JPA et l'API JPA.

Avertissement : La version 2.x du plug-in DataNucleus pour App Engine utilise DataNucleus en version 3.x. Le plug-in 2.x n'est pas totalement rétrocompatible avec le plug-in précédent 1.x. Si vous effectuez une mise à niveau vers la nouvelle version, veillez à mettre à jour votre application et à la tester.

Compiler des outils compatibles avec JPA 2.x et 3.0

Vous pouvez utiliser Apache Ant ou Maven pour utiliser la version 2.x ou 3.0 du plug-in DataNucleus pour App Engine :

  • Pour les utilisateurs d'Ant : le SDK comprend une tâche Ant qui effectue la phase d'enrichissement. Vous devez copier les fichiers JAR et créer le fichier de configuration au moment où vous configurez votre projet.
  • Pour les utilisateurs de Maven : vous pouvez enrichir les classes avec les configurations suivantes dans votre fichier pom.xml :
                <plugin>
                    <groupId>org.datanucleus</groupId>
                    <artifactId>maven-datanucleus-plugin</artifactId>
                    <version>3.2.0-m1</version>
                    <configuration>
                        <api>JDO</api>
                        <props>${basedir}/datanucleus.properties</props>
                        <verbose>true</verbose>
                        <enhancerName>ASM</enhancerName>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>process-classes</phase>
                            <goals>
                                <goal>enhance</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>org.datanucleus</groupId>
                            <artifactId>datanucleus-api-jdo</artifactId>
                            <version>3.1.3</version>
                        </dependency>
                    </dependencies>
                </plugin>

Migrer vers la version 2.x du plug-in DataNucleus

Cette section fournit des instructions vous permettant de mettre à niveau votre application pour qu'elle utilise la version 2.x du plug-in DataNucleus pour App Engine, qui correspond à la plate-forme d'accès DataNucleus 3.0 et à JPA 2.0. La version 2.x du plug-in n'est pas totalement rétrocompatible avec la version 1.x, et elle est susceptible d'être modifiée sans préavis. Si vous effectuez une mise à niveau, veillez à mettre à jour le code de votre application et à le tester.

Nouveaux comportements par défaut

La version 2.x du plug-in DataNucleus pour App Engine possède des valeurs par défaut différentes de la version précédente 1.x :

  • Le "fournisseur de persistance" JPA est désormais org.datanucleus.api.jpa.PersistenceProviderImpl.
  • La mise en cache de niveau 2 est activée par défaut. Pour obtenir le comportement par défaut précédent, définissez la propriété de persistance datanucleus.cache.level2.type sur none. (Vous pouvez également inclure le plug-in datanucleus-cache dans le chemin de classe et définir la propriété de persistance datanucleus.cache.level2.type sur javax.cache pour utiliser Memcache pour la mise en cache L2.)
  • Datastore IdentifierFactory est désormais configuré par défaut sur datanucleus2. Pour obtenir le comportement précédent, définissez la propriété de persistance datanucleus.identifierFactory sur datanucleus1.
  • Les appels non transactionnels vers EntityManager.persist(), EntityManager.merge() et EntityManager.remove() sont désormais exécutés de manière atomique. (Auparavant, l'exécution était effectuée lors de la transaction suivante ou avec l'appel de EntityManager.close().)
  • La fonction retainValues est activée pour JPA, ce qui signifie que les valeurs des champs chargés sont conservées dans les objets après une validation.
  • La fonction javax.persistence.query.chunkSize n'est plus utilisée. Utilisez plutôt datanucleus.query.fetchSize.
  • Il n'y a plus d'exception sur l'allocation EMF en double. Si la propriété de persistance datanucleus.singletonEMFForName est définie sur la valeur true, alors elle renvoie l'objet EMF singleton actuellement alloué pour ce nom.
  • Les relations sans propriétaire sont désormais prises en charge.
  • Datastore Identity est désormais pris en charge.

Pour obtenir la liste complète des nouvelles fonctionnalités, consultez les notes de version.

Modifications des fichiers de configuration

Pour mettre à niveau votre application afin qu'elle utilise la version 2.0 du plug-in DataNucleus pour App Engine, vous devez modifier certains paramètres de configuration dans build.xml et persistence.xml. Si vous configurez une nouvelle application et souhaitez utiliser la dernière version du plug-in DataNucleus, passez à la section Configurer JPA 2.0.

Avertissement : Après avoir mis à jour votre configuration, vous devez tester le code de votre application pour en garantir la rétrocompatibilité.

Dans build.xml

La cible copyjars doit être modifiée pour assurer la compatibilité avec DataNucleus 2.x :

  1. La cible copyjars a été modifiée. Modifiez la section ci-après :
      <target name="copyjars"
          description="Copies the App Engine JARs to the WAR.">
        <mkdir dir="war/WEB-INF/lib" />
        <copy
            todir="war/WEB-INF/lib"
            flatten="true">
          <fileset dir="${sdk.dir}/lib/user">
            <include name="**/*.jar" />
          </fileset>
        </copy>
      </target>

    pour qu'elle ressemble à ceci :
      <target name="copyjars"
          description="Copies the App Engine JARs to the WAR.">
        <mkdir dir="war/WEB-INF/lib" />
        <copy
            todir="war/WEB-INF/lib"
            flatten="true">
          <fileset dir="${sdk.dir}/lib/user">
            <include name="**/appengine-api-1.0-sdk*.jar" />
          </fileset>
          <fileset dir="${sdk.dir}/lib/opt/user">
            <include name="appengine-api-labs/v1/*.jar" />
            <include name="jsr107/v1/*.jar" />
            <include name="datanucleus/v2/*.jar" />
          </fileset>
        </copy>
      </target>
  2. La cible datanucleusenhance a été modifiée. Modifiez la section ci-après :
      <target name="datanucleusenhance" depends="compile"
          description="Performs enhancement on compiled data classes.">
        <enhance_war war="war" />
      </target>

    pour qu'elle ressemble à ceci :
      <target name="datanucleusenhance" depends="compile"
          description="Performs enhancement on compiled data classes.">
          <enhance_war war="war">
                  <args>
                  <arg value="-enhancerVersion"/>
                  <arg value="v2"/>
              </args>
          </enhance_war>
      </target>

Dans persistence.xml

La cible <provider> a été modifiée. Mettez à jour la section suivante :

        <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>

vers :

        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>

Configurer JPA 2.0

Pour accéder au magasin de données au moyen de JPA, une application App Engine a besoin que les conditions suivantes soient remplies :

  • Les fichiers JAR de JPA et du datastore doivent se trouver dans le répertoire war/WEB-INF/lib/ de l'application.
  • Un fichier de configuration nommé persistence.xml, indiquant à JPA d'utiliser le datastore App Engine, doit être stocké dans le répertoire war/WEB-INF/classes/META-INF/ de l'application.
  • Le processus de compilation du projet doit exécuter une étape "d'enrichissement" post-compilation sur les classes de données compilées pour les associer à la mise en œuvre JPA.

Copie des fichiers JAR

Les fichiers JAR de JPA et du magasin de données sont disponibles dans le SDK Java App Engine. Ils sont stockés dans le répertoire appengine-java-sdk/lib/opt/user/datanucleus/v2/.

Copiez les fichiers JAR dans le répertoire war/WEB-INF/lib/ de votre application.

Assurez-vous que le fichier appengine-api.jar se trouve également dans le répertoire war/WEB-INF/lib/. (Vous l'avez peut-être déjà copié au moment de la création du projet.) Le plug-in DataNucleus App Engine utilise ce fichier JAR pour accéder au magasin de données.

Création du fichier persistence.xml

L'interface JPA requiert la présence d'un fichier de configuration nommé persistence.xml dans le répertoire de l'application war/WEB-INF/classes/META-INF/. Vous pouvez créer directement ce fichier à cet emplacement, ou paramétrer votre processus de compilation afin de copier ce fichier à partir d'un répertoire source.

Créez le fichier en y incluant le contenu suivant :

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="transactions-optional">
        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
            <property name="datanucleus.singletonEMFForName" value="true"/>
        </properties>

    </persistence-unit>

</persistence>

Règles de lecture et durée maximale de l'appel au Datastore

Comme indiqué sur la page Requêtes Datastore, vous pouvez définir la règle de lecture (cohérence forte ou cohérence à terme) et le délai d'appel du datastore pour EntityManagerFactory dans le fichier persistence.xml. Ces paramètres sont stockés dans l'élément <persistence-unit>. Tous les appels effectués avec une instance EntityManager donnée utilisent la configuration sélectionnée au moment de la création du gestionnaire par la classe EntityManagerFactory. Vous pouvez également ignorer ces options pour un objet Query spécifique (comme décrit ci-après).

Pour définir la règle de lecture, spécifiez une propriété nommée datanucleus.appengine.datastoreReadConsistency. Ses valeurs possibles sont EVENTUAL (pour les lectures avec cohérence à terme) et STRONG (pour les lectures avec cohérence forte). Si aucune valeur n'est spécifiée, la valeur par défaut de cet argument est STRONG.

            <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />

Le délai d'appel vers le datastore que vous définissez pour les lectures peut différer de celui défini pour les écritures. Pour les lectures, utilisez la propriété standard JPA javax.persistence.query.timeout. Pour les écritures, utilisez datanucleus.datastoreWriteTimeout. La valeur du délai d'appel correspond à une durée exprimée en millisecondes.

            <property name="javax.persistence.query.timeout" value="5000" />
            <property name="datanucleus.datastoreWriteTimeout" value="10000" />

Si vous souhaitez utiliser des transactions entre groupes (XG), ajoutez la propriété suivante :

            <property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />

Vous pouvez avoir dans le même fichier persistence.xml plusieurs éléments <persistence-unit> utilisant différents attributs name, afin d'exploiter des instances EntityManager ayant des configurations différentes dans la même application. Par exemple, le fichier persistence.xml suivant établit deux ensembles de configuration, l'un nommé "transactions-optional" et l'autre nommé "eventual-reads-short-deadlines" :

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="transactions-optional">
        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
        </properties>
    </persistence-unit>

    <persistence-unit name="eventual-reads-short-deadlines">
        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>

            <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
            <property name="javax.persistence.query.timeout" value="5000" />
            <property name="datanucleus.datastoreWriteTimeout" value="10000" />
            <property name="datanucleus.singletonEMFForName" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

Pour en savoir plus sur la création d'une instance EntityManager avec un ensemble de configuration nommé, reportez-vous à la section Récupérer une instance EntityManager ci-dessous.

Vous pouvez passer outre la règle de lecture et le délai d'appel pour un objet Query spécifique. Pour ignorer la règle de lecture pour un objet Query, appelez sa méthode setHint() à l'aide du code suivant :

        Query q = em.createQuery("select from " + Book.class.getName());
        q.setHint("datanucleus.appengine.datastoreReadConsistency", "EVENTUAL");

Comme indiqué ci-dessus, les valeurs possibles de cette règle sont "EVENTUAL" et "STRONG".

Pour passer outre le délai de lecture, appelez la méthode à l'aide du code setHint() suivant :

        q.setHint("javax.persistence.query.timeout", 3000);

Il n'existe aucun moyen d'ignorer la configuration de ces options lorsque vous récupérez des entités par leur clé.

Enrichissement du code des classes de données

La mise en œuvre DataNucleus de JPA ajoute au processus de compilation une étape "d'enrichissement" post-compilation, afin d'associer des classes de données à la mise en œuvre de JPA.

Pour effectuer l'opération d'enrichissement sur des classes compilées, exécutez la commande ci-après à partir de la ligne de commande :

java -cp classpath org.datanucleus.enhancer.DataNucleusEnhancer
class-files

Le paramètre classpath doit inclure les fichiers JAR datanucleus-core-*.jar, datanucleus-jpa-*, datanucleus-enhancer-*.jar, asm-*.jar et geronimo-jpa-*.jar (où * correspond au numéro de version approprié de chaque fichier JAR) situés dans le répertoire appengine-java-sdk/lib/tools/, ainsi que l'ensemble de vos classes de données.

Pour en savoir plus sur l'outil d'enrichissement de bytecode DataNucleus, consultez la documentation de DataNucleus.

Récupérer une instance EntityManager

Une application interagit avec JPA à l'aide d'une instance de la classe EntityManager. Pour récupérer cette instance, vous devez instancier la classe EntityManagerFactory et appeler une méthode sur cet objet. La fabrique utilise la configuration JPA (identifiée par le nom "transactions-optional") pour créer des instances EntityManager.

Étant donné qu'une instance EntityManagerFactory met du temps à s'initialiser, il est judicieux de réutiliser une même instance autant que possible. Un moyen simple d'effectuer cette opération consiste à créer une classe wrapper singleton avec une instance statique à l'aide du code suivant :

EMF.java

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class EMF {
    private static final EntityManagerFactory emfInstance =
        Persistence.createEntityManagerFactory("transactions-optional");

    private EMF() {}

    public static EntityManagerFactory get() {
        return emfInstance;
    }
}

Conseil : "transactions-optional" fait référence au nom de l'ensemble de configuration défini dans le fichier persistence.xml. Si votre application utilise plusieurs ensembles de configuration, vous devrez étendre ce code pour qu'il appelle la méthode Persistence.createEntityManagerFactory() autant de fois que nécessaire. Votre code doit mettre en cache une instance singleton de chaque instance EntityManagerFactory.

L'application utilise l'instance de fabrique afin de créer une instance EntityManager pour chaque requête qui accède au datastore.

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import EMF;

// ...
    EntityManager em = EMF.get().createEntityManager();

Utilisez l'instance EntityManager pour stocker, mettre à jour et supprimer des objets de données, ainsi que pour exécuter des requêtes datastore.

Lorsque vous avez fini d'utiliser l'instance EntityManager, vous devez appeler sa méthode close(). Vous ne devez pas utiliser l'instance EntityManager après avoir appelé sa méthode close().

    try {
        // ... do stuff with em ...
    } finally {
        em.close();
    }

Annotations de classe et de champ

Chaque objet enregistré par JPA devient une entité du magasin de données App Engine. Le genre de l'entité est dérivé du nom simple de la classe (sans le nom du package). Chaque champ persistant de la classe représente une propriété de l'entité, dont le nom correspond au nom du champ (en respectant la distinction majuscules-minuscules).

Pour indiquer qu'une classe Java peut être stockée et extraite du magasin de données à l'aide de JPA, associez à cette classe une annotation @Entity. Exemple :

import javax.persistence.Entity;

@Entity
public class Employee {
    // ...
}

Les champs de la classe de données qui doivent être stockés dans le magasin de données doivent être d'un type persistant par défaut ou être explicitement déclarés comme persistants. Vous trouverez un graphique détaillant le comportement de persistance par défaut de JPA sur le site Web de DataNucleus. Pour déclarer explicitement un champ comme persistant, attribuez-lui une annotation @Basic :

import java.util.Date;
import javax.persistence.Enumerated;

import com.google.appengine.api.datastore.ShortBlob;

// ...
    @Basic
    private ShortBlob data;

Les types de champs possibles sont répertoriés ci-dessous :

  • L'un des types de base pris en charge par le magasin de données
  • Une collection (par exemple, java.util.List<...>) de valeurs d'un type de datastore de base
  • Une instance ou une collection d'instances d'une classe @Entity
  • Une classe encapsulée, stockée sous forme de propriétés dans l'entité

Une classe de données doit disposer d'un constructeur par défaut public ou protégé, ainsi que d'un champ dédié au stockage de la clé primaire de l'entité correspondante dans le magasin de données. Vous pouvez choisir parmi quatre genres de champs de clé, chacun utilisant un type de valeur et des annotations distincts. (Pour plus d'informations, consultez la page Créer des données : clés.) Le champ de clé le plus simple correspond à un entier long qui est automatiquement renseigné par JPA avec une valeur unique pour l'ensemble des instances de la classe lorsque l'objet est enregistré pour la première fois dans le magasin de données. Les clés de type entier long utilisent une annotation @Id, ainsi qu'une annotation @GeneratedValue(strategy = GenerationType.IDENTITY) :

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

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

// ...
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

Voici un exemple de classe de données :

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

import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    private String firstName;

    private String lastName;

    private Date hireDate;

    // Accessors for the fields. JPA doesn't use these, but your application
    does.

    public Key getKey() {
        return key;
    }

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Date getHireDate() {
        return hireDate;
    }
    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    }
}

Héritage

JPA prend en charge la création de classes de données qui utilisent des stratégies d'héritage. Avant d'aborder le fonctionnement de l'héritage JPA dans App Engine, nous vous recommandons de consulter la documentation DataNucleus sur ce sujet, puis de reprendre votre lecture de cette documentation. C'est fait ? D'accord. L'héritage JPA dans App Engine fonctionne comme décrit dans la documentation de DataNucleus, avec quelques restrictions supplémentaires. Cette section spécifie ces restrictions, puis en présente quelques exemples concrets.

La stratégie d'héritage "JOINED" (par jointure) vous permet de fractionner les données d'un même objet de données dans différentes "tables". Toutefois, du fait que le magasin de données d'App Engine n'est pas compatible avec les jointures, l'utilisation d'un objet de données avec cette stratégie d'héritage nécessite un appel de procédure à distance pour chaque niveau d'héritage. C'est un fonctionnement potentiellement très inefficace. Par conséquent, la stratégie d'héritage "JOINED" n'est pas compatible avec les classes de données.

Par ailleurs, la stratégie d'héritage "SINGLE_TABLE" (table unique) vous permet de stocker les données d'un objet de données dans une "table" unique associée à la classe persistante figurant à la racine de votre hiérarchie d'héritage. Bien que cette stratégie ne présente pas de risque d'inefficacité, elle n'est pas prise en charge à ce jour. Cet aspect sera éventuellement modifié dans les versions ultérieures.

Maintenant, voici la bonne nouvelle : les stratégies "TABLE_PER_CLASS" et "MAPPED_SUPERCLASS" fonctionnent comme décrit dans la documentation de DataNucleus. Voyons un exemple :

Worker.java

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

@Entity
@MappedSuperclass
public abstract class Worker {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;

    private String department;
}

Employee.java

// ... imports ...

@Entity
public class Employee extends Worker {
    private int salary;
}

Intern.java

import java.util.Date;
// ... imports ...

@Entity
public class Intern extends Worker {
    private Date internshipEndDate;
}

Dans cet exemple, nous avons ajouté une annotation @MappedSuperclass à la déclaration de classe Worker. Cela indique à JPA de stocker tous les champs persistants de la classe Worker dans les entités du magasin de données correspondant à ses sous-classes. L'entité du datastore créée à la suite de l'appel de persist() avec une instance Employee aura deux propriétés nommées "department" "et "salary". L'entité du datastore créée à la suite de l'appel de persist() sur une instance Intern possède deux propriétés nommées "department" et "internshipEndDate". Le magasin de données ne contient aucune entité du genre "Worker".

Passons maintenant à un aspect un peu plus intéressant. Supposons qu'en plus des instances Employee et Intern, nous souhaitions également disposer d'une spécialisation de l'instance Employee décrivant les employés qui ont quitté l'entreprise :

FormerEmployee.java

import java.util.Date;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
// ... imports ...

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class FormerEmployee extends Employee {
    private Date lastDay;
}

Dans cet exemple, nous avons ajouté une annotation @Inheritance à la déclaration de classe FormerEmployee en définissant son attribut strategy sur InheritanceType.TABLE_PER_CLASS. Cette opération indique à JPA de stocker tous les champs persistants de la classe FormerEmployee et de ses superclasses dans les entités du magasin de données correspondant aux instances FormerEmployee. L'entité du datastore créée à la suite de l'appel de persist() sur une instance FormerEmployee possède trois propriétés nommées "department", "salary" et "lastDay". Il n'y aura jamais d'entité du genre "Employee" qui correspond à un FormerEmployee, mais si vous appelez persist() avec un objet dont le type d'exécution est Employee, vous créez une entité du genre "Employee".

L'utilisation combinée de relations et de stratégies d'héritage fonctionne tant que les types déclarés de vos champs de relations correspondent aux types d'exécution des objets que vous attribuez à ces champs. Pour plus d'informations, reportez-vous à la section sur les relations polymorphes. Bien que cette section présente des exemples relatifs à JDO, les concepts et les restrictions qui s'appliquent à JDO sont les mêmes que pour JPA.

Fonctionnalités non compatibles de JPA 2.0

Les fonctionnalités de l'interface JPA non prises en charge par l'implémentation App Engine sont les suivantes :

  • Relations d'appartenance plusieurs à plusieurs.
  • Requêtes "Join". Vous ne pouvez pas utiliser un champ d'une entité enfant dans un filtre lorsque vous exécutez une requête sur le genre parent. Notez que vous pouvez tester directement le champ de relation du parent dans une requête à l'aide d'une clé.
  • Requêtes d'agrégation (GROUP BY, HAVING, SUM, AVG, MAX, MIN).
  • Requêtes polymorphes. Vous ne pouvez pas exécuter de requête d'une classe pour récupérer des instances d'une sous-classe. Chaque classe est représentée par un genre d'entité distinct dans le datastore.