Définition d'un schéma d'entité

Un ObjectGrid peut posséder un nombre quelconque de schémas d'entité logiques. Les entités sont définies à l'aide de classes Java annotées, d'un XML ou d'une combinaison d'un XML et de classes Java. Les entités définies sont ensuite enregistrées sur un serveur eXtreme Scale et associées à BackingMaps, à des index et d'autres plug-in.

Lorsque vous concevez un schéma d'entité, vous devez effectuer les tâches suivantes :
  1. Définissez les entités et leurs relations.
  2. Configurez eXtreme Scale.
  3. Enregistrez les entités.
  4. Créez des applications basées sur des entités qui interagissent avec les API EntityManager d'eXtreme Scale.

Configuration d'un schéma d'entité

Un schéma d'entité est composé d'un ensemble d'entités et des relations entre ces entités. Dans une application eXtreme Scale comportant plusieurs partitions, les restrictions et les options suivantes s'appliquent aux schémas d'entité : Les entités sont enregistrées avec une instance ObjectGrid avant qu'elle ne soit initialisée. Chaque entité définie doit posséder un nom unique et est automatiquement associée à une mappe de sauvegarde ObjectGrid de même nom. La méthode d'initialisation varie en fonction de la configuration que vous utilisez :

Configuration eXtreme Scale locale

Si vous utilisez un ObjectGrid local, vous pouvez configurer le schéma d'entité à l'aide d'un programme. Dans ce mode, vous pouvez utiliser les méthodes ObjectGrid.registerEntities pour enregistrer les classes d'entité annotées ou un fichier de descripteur de métadonnées d'entité.

Configuration eXtreme Scale répartie

Si vous utilisez une configuration eXtreme Scale répartie, vous devez fournir un fichier de descripteur de métadonnées d'entité avec le schéma d'entité.

Pour plus d'informations, reportez-vous à la rubrique Gestionnaire d'entités dans un environnement distribué.

Exigences des entités

Les métadonnées d'entité sont configurées à l'aide de fichiers de classe Java et/ou d'un fichier XML de descripteur d'entité. Le XML du descripteur d'entité au moins est requis pour identifier les mappes de sauvegarde eXtreme Scale associées à des entités. Les attributs persistants de l'entité et ses relations avec les autres entités sont décrits dans une classe Java annotée (classe de métadonnées d'entité) ou dans le fichier XML du descripteur d'entité. La classe de métadonnées d'entité, si elle est spécifiée, est également utilisée par l'API EntityManager pour interagir avec les données de la grille.

Une grille eXtreme Scale peut être définie sans fournir de classes d'entité. Cela peut être avantageux si le serveur et le client interagissent directement avec les données de nuplet stockées dans les mappes sous-jacentes. De telles entités sont intégralement définies dans le fichier XML du descripteur d'entité et sont appelées entités sans classe.

Entités sans classe

Les entités sans classe sont utiles s'il n'est pas possible d'inclure des classes d'application dans le chemin d'accès aux classes du serveur ou du client. De telles entités sont définies dans le fichier XML du descripteur de métadonnées d'entité, où le nom de classe de l'entité est spécifié à l'aide d'un identificateur d'entité sans classe de la forme suivante : @<identificateur d'entité>. Le symbole @ identifie l'entité comme sans classe et est utilisé pour les associations de mappage entre les entités. Pour un exemple de fichier XML de descripteur de métadonnées d'entité avec deux entités sans classe définies, voir la figure "Métadonnées d'entité sans classe".

Si un serveur ou un client eXtreme Scale n'a pas accès aux classes, il peut quand même utiliser l'API EntityManager à l'aide d'entités sans classe. Les cas d'utilisation courants sont les suivants :

  • Le conteneur eXtreme Scale est hébergé sur un serveur qui n'autorise pas les classes d'application dans le chemin d'accès aux classes. Dans ce cas, les clients peuvent toujours accéder à la grille à l'aide de l'API EntityManager à partir d'un client, où les classes sont autorisées.
  • Le client eXtreme Scale ne nécessite pas un accès aux classes d'entité car il utilise un client non Java tel que le service de données REST d'eXtreme Scale ou il accède aux données de nuplet de la grille à l'aide de l'API ObjectMap.

Si les métadonnées d'entité sont compatibles entre le client et le serveur, elles peuvent être créées à l'aide de classes de métadonnées d'entité et/ou d'un fichier XML.

Par exemple, la "classe d'entité par programmation" de la figure ci-après est compatible avec le code des métadonnées sans classe de la section suivante.

Classe d'entité par
programmation
@Entity
public class Employee {
    @Id long serialNumber;
    @Basic byte[] picture;
    @Version int ver;
    @ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.PERSIST)
    Department department;
}

@Entity
public static class Department {
    @Id int number;
    @Basic String name;
    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="department")
    Collection<Employee> employees;
}

Zones sans classe, clés et versions

Comme indiqué précédemment, les entités sans classe sont configurées intégralement dans le fichier de descripteur XML d'entité. Les entités basées sur des classes définissent leurs attributs à l'aide d'annotations, de propriétés et de zones Java. Par conséquent, les entités sans classe doivent définir une structure de clés et d'attributs dans le descripteur XML d'entité à l'aide des balises <basic> et <id>.

Métadonnées
d'entité sans classe
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://ibm.com/ws/projector/config/emd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://ibm.com/ws/projector/config/emd ./emd.xsd">

<entity class-name="@Employee" name="Employee">
    <attributes>
        <id name="serialNumber" type="long"/>
        <basic name="firstName" type="java.lang.String"/>
        <basic name="picture" type="[B"/>
        <version name="ver" type="int"/>
        <many-to-one 
            name="department" 
            target-entity="@Department"
            fetch="EAGER"">
                <cascade><cascade-persist/></cascade>
        </many-to-one>
    </attributes>
</entity>

<entity class-name="@Department" name="Department" >
    <attributes>
        <id name="number" type="int"/>
        <basic name="name" type="java.lang.String"/>
        <version name="ver" type="int"/>
        <one-to-many 
            name="employees" 
            target-entity="@Employee" 
            fetch="LAZY" 
            mapped-by="department">
            <cascade><cascade-all/></cascade>
        </one-to-many>
    </attributes>
</entity>

Notez que chaque entité ci-dessus contient un élément <id>. Une entité sans classe doit posséder un ou plusieurs éléments <id> ou une association à valeur unique qui représente la clé de l'entité. Les zones de l'entité sont représentées par des éléments <basic>. Les éléments <id>, <version> et <basic> requièrent un nom et un type dans les entités sans classe. Pour des détails sur les types pris en charge, reportez-vous à la section sur les types d'attribut pris en charge.

Exigences des classes d'entité

Les entités basées sur des classes sont identifiées en associant diverses métadonnées à une classe Java. Les métadonnées peuvent être spécifiées à l'aide d'annotations Java Platform, Standard Edition 5 et/ou d'un fichier descripteur de métadonnées d'entité. Les classes d'entité doivent satisfaire les critères suivants : Les entités possèdent toutes un nom et un type uniques. Si des annotations sont utilisées, le nom correspond au nom simple (short) de la classe par défaut, mais il peut être remplacé à l'aide de l'attribut de nom de l'annotation @Entity.

Attributs persistants

L'état persistant d'une entité est accessible par les clients et le gestionnaire d'entités à l'aide d'accesseurs aux zones (variables d'instance) ou aux propriété (style EJB). Chaque entité doit définir un accès par zone ou par propriété. Les entités annotées sont accessibles par des zones si les zones de la classe sont annotées ou accessibles par des propriétés si la méthode d'accès get de la propriété est annotée. Il n'est pas possible de mélanger les accès par zone et les accès par propriété. Si le type ne peut pas être déterminé automatiquement, l'attribut accessType de l'annotation @Entity ou le XML équivalent peut être utilisé pour identifier le type d'accès.

Zones persistantes
Les variables d'instance d'entité accessibles par zone sont accessibles directement à partir du gestionnaire d'entités et des clients. Les zones marquées avec le modificateur transitoire ou l'annotation transitoire sont ignorées. Les zones persistantes ne doivent pas contenir de modificateurs final ou static.
Propriétés persistantes
Les entités d'accès aux propriétés doivent respecter les conventions de signature des JavaBeans pour les propriétés de lecture et d'écriture. Les méthodes qui ne respectent pas les conventions des JavaBeans ou pour lesquelles l'annotation Transitoire se trouve sur la méthode d'accès get sont ignorées. Pour une propriété de type T, il doit exister une méthode d'accès get getProperty qui renvoie une valeur de type T et une méthode d'accès set setProperty(T) nulle. Pour les types booléens, la méthode d'accès get peut être exprimé sous la forme isProperty et renvoyer la valeur true ou false. Les propriétés persistantes ne peuvent pas posséder le modificateur statique.
Types d'attribut pris en charge
Les types suivants de propriété et de zone persistante sont pris en charge :
  • Les types de primitive Java incluent les encapsuleurs
  • java.lang.String
  • java.math.BigInteger
  • java.math.BigDecimal
  • java.util.Date
  • java.util.Calendar
  • java.sql.Date
  • java.sql.Time
  • java.sql.Timestamp
  • byte[]
  • java.lang.Byte[]
  • char[]
  • java.lang.Character[]
  • enum
Les types d'attribut sérialisables par l'utilisateur sont pris en charge mais connaissent des limitations en termes de performances, de requête et de détection des modifications. Les données persistantes qui ne peuvent pas avoir de proxys, tels que les tableaux et les objets sérialisables par l'utilisateur, doivent être réaffectées à l'entité si elles sont modifiées.

Les attributs sérialisables sont représentés dans le fichier XML du descripteur d'entité à l'aide du nom de classe de l'objet. Si l'objet correspond à un tableau, le type de données est représenté à l'aide du format Java interne. Par exemple, si un type de données d'attribut est java.lang.Byte[][], la représentation de la chaîne est la suivante : [[Ljava.lang.Byte;

Les types sérialisables par l'utilisateur doivent respecter les meilleures pratiques suivantes :

  • Implémentez des méthodes de sérialisation à hautes performances. Implémentez l'interface java.lang.Cloneable et la méthode publique clone.
  • Implémentez l'interface java.io.Externalizable.
  • Implémentez les méthodes equals et hashCode

Associations d'entités

Les associations d'entités bidirectionnelles et unidirectionnelles, ou relations entre les entités, peuvent être définies comme des associations one-to-one, many-to-one, one-to-many et many-to-many. Le gestionnaire d'entités convertit automatiquement les relations d'entité dans les références de clé appropriées lorsqu'il stocke les entités.

La grille eXtreme Scale est un cache de données et n'applique pas l'intégrité référentielle comme le fait une base de données. Les relations permettent les opérations de stockage et de suppression en cascade pour les entités enfant, mais elles ne détectent pas les liens rompus et ne les appliquent pas aux objets. Lors de la suppression d'un objet enfant, la référence à cet objet doit être supprimée du parent.

Si vous définissez une association bidirectionnelle entre deux entités, vous devez identifier le propriétaire de la relation. Dans une association to-many, la partie "many" de la relation est toujours la partie propriétaire. Si la propriété ne peut pas être déterminée automatiquement, l'attribut mappedBy de l'annotation ou son équivalent XML doit être spécifié. L'attribut mappedBy identifie la zone dans l'entité cible propriétaire de la relation. Cet attribut permet également d'identifier les zones en rapport lorsqu'il existe plusieurs attributs de même type et de même cardinalité.

Associations à valeur unique

Les associations one-to-one et many-to-one sont dénotées à l'aide des annotations @OneToOne et @ManyToOne ou des attributs XML équivalents. Le type d'entité cible est déterminé par le type d'attribut. L'exemple ci-après définit une association unidirectionnelle entre Person et Address. L'entité Customer possède une référence à une entité Address. Dans ce cas, il peut également s'agir d'une association many-to-one car il n'existe pas de relation inverse.
@Entity
public class Customer {
  @Id id;
  @OneToOne Address homeAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
}
Pour spécifier une relation bidirectionnelle entre les classes Customer et Address, ajoutez une référence à la classe Customer à partir de la classe Address et ajoutez l'annotation appropriée pour marquer la partir inverse de la relation. Comme il s'agit d'une association one-to-one, vous devez spécifier un propriétaire de la relation en utilisant l'attribut mappedBy sur l'annotation @OneToOne.
@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToOne(mappedBy="homeAddress") Customer customer;
}

Associations évaluées par des collections

Les associations one-to-one et many-to-many sont dénotées à l'aide des annotations @OneToMany et @ManyToMany ou des attributs XML équivalents. Toutes les relations "many" sont représentées à l'aide des types suivants : java.util.Collection, java.util.List ou java.util.Set. Le type d'entité cible est déterminé par le type générique de la collection, de la liste ou de l'ensemble ou explicitement en utilisant l'attribut targetEntity sur l'annotation @OneToMany ou @ManyToMany (ou son équivalent XML).
Dans l'exemple précédent, il n'est pas pratique d'avoir un objet d'adresse par client car de nombreux clients peuvent partager une adresse ou posséder plusieurs adresses. Il est préférable d'utiliser une association "many" :
@Entity
public class Customer {
  @Id id;
  @ManyToOne Address homeAddress;
  @ManyToOne Address workAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToMany(mappedBy="homeAddress") Collection<Customer> homeCustomers;


  @OneToMany(mappedBy="workAddress", targetEntity=Customer.class) 
		Collection workCustomers;
}
Dans cet exemple, il existe deux relations entre les mêmes entités : une relation d'adresse entre Home et Work. Une collection non générique est utilisée pour l'attribut workCustomers afin d'illustrer l'utilisation de l'attribut targetEntity lorsqu'aucun générique n'est disponible.

Associations sans classe

Les associations d'entités sans classe sont définies dans le fichier XML du descripteur de métadonnées d'entité, de la même manière que les associations basées sur des classes. Toutefois, l'entité cible ne pointe pas vers une classe réelle, mais vers un identificateur d'entité sans classe utilisé pour le nom de classe de l'entité.

Voici un exemple :

<many-to-one name="department" target-entity="@Department" fetch="EAGER">
      <cascade><cascade-all/></cascade>
</many-to-one>
<one-to-many name="employees" target-entity="@Employee" fetch="LAZY">
      <cascade><cascade-all/></cascade>
</one-to-many>

Clés primaires

Toutes les entités doivent posséder une clé primaire, qui peut être une clé simple (un attribut) ou composée (plusieurs attributs). Les attributs de clé sont signalés à l'aide de l'annotation Id ou définis dans le fichier XML du descripteur d'entité. Les exigences suivantes s'appliquent à ces attributs de clé : Les clés primaires composées peuvent éventuellement définir une classe de clé primaire. Une entité est associée à une classe de clé primaire à l'aide de l'annotation @IdClass ou du fichier XML de descripteur d'entité. Une annotation @IdClass est utilise lorsqu'elle est utilisée conjointement avec la méthode EntityManager.find.
Les classes de clé primaire sont soumises aux exigences suivantes :
  • Elles doivent être publiques avec un constructeur sans argument.
  • Le type d'accès de la classe de clé primaire est déterminé par l'entité qui déclare la classe de clé primaire.
  • Si elles sont accessibles par des propriétés, les propriétés de la classe de clé primaire doivent être publiques ou protégées.
  • Les propriétés ou les zones des clés primaires doivent correspondre aux noms et aux types d'attribut de clé définis dans l'entité qui y font référence.
  • Les classes de clé primaire doivent implémenter les méthodes equals et hashCode.
Voici un exemple :
@Entity
@IdClass(CustomerKey.class)
public class Customer {
    @Id @ManyToOne Zone zone;
    @Id int custId;
    String name;
    ...
}

@Entity
public class Zone{
    @Id String zoneCode;
    String name;
}

public class CustomerKey {
    Zone zone;
    int custId;

    public int hashCode() {...}
    public boolean equals(Object o) {...}
}

Clés primaires sans classe

Les entités sans classe doivent contenir au moins un élément <id> ou une association dans le fichier XML avec l'attribut id=true. Voici un exemple de ces deux cas de figure :

<id name="serialNumber" type="int"/>
<many-to-one name="department" target-entity="@Department" id="true">
<cascade><cascade-all/></cascade>
</many-to-one>
A faire :
La balise XML <id-class> n'est pas prise en charge pour les entités sans classe.

Interception des zones et des proxys d'entité

Les classes d'entité et les types d'attribut modifiables pris en charge sont étendus par des classes proxy pour les entités accessibles par des propriétés et leur bytecode est étendu pour les entités Java Development Kit (JDK) 5 accessibles par zone. Tous les accès à l'entité, même par des méthodes métier internes et par les méthodes equals, doivent utiliser les méthodes appropriées d'accès par propriété ou par zone.

Les proxys et les intercepteurs de zone sont utilisés pour permettre au gestionnaire d'entités de rechercher l'état de l'entité, de déterminer si l'entité a été modifiée et d'améliorer les performances. Les intercepteurs de zone ne sont disponibles que sur les plateformes Java SE 5 si l'agent d'instrumentation des entités est configuré.

Avertissement : Si des entités d'accès aux propriétés sont utilisées, la méthode equals doit utiliser l'opérateur instanceof pour comparer l'instance actuelle à l'objet d'entrée. Toute introspection de l'objet cible doit être effectuée via les propriétés de l'objet et non via les zones elles-mêmes, car l'instance d'objet correspondra au proxy.