Entitätsschema definieren

Ein ObjectGrid kann eine beliebige Anzahl logischer Entitätsschemas haben. Entitäten werden über annotierte Java-Klassen, XML oder eine Kombination von XML und Java-Klassen definiert. Definierte Entitäten werden anschließend bei einem Server von eXtreme Scale registriert und an BackingMaps, Indizes und andere Plug-ins gebunden.

Beim Design eines Entitätsschemas müssen Sie die folgenden Tasks ausführen:
  1. Entitäten und ihre Beziehungen definieren,
  2. eXtreme Scale konfigurieren,
  3. Entitäten registrieren,
  4. entitätsbasierte Anwendungen erstellen, die mit den EntityManager-APIs von eXtreme Scale interagieren.

Konfiguration des Entitätsschemas

Ein Entitätsschema setzt sich aus einer Gruppe von Entitäten und den Beziehungen zwischen diesen Entitäten zusammen. In einer eXtreme-Scale-Anwendung mit mehreren Partitionen gelten die folgenden Einschränkungen und Optionen für Entitätsschemas: Entitäten werden bei einer ObjectGrid-Instanz registriert, bevor sie initialisiert werden. Jede definierte Entität muss eindeutig benannt werden und wird automatisch an eine ObjectGrid-BackingMap desselben Namens gebunden. Die Initialisierungsmethode variiert je nach verwendeter Konfiguration:

Lokale Konfiguration von eXtreme Scale

Wenn Sie ein lokales ObjectGrid verwenden, können Sie das Entitätsschema über das Programm konfigurieren. In diesem Modus können Sie die Methoden ObjectGrid.registerEntities verwenden, um annotierte Entitätsklassen oder Deskriptordatei für die Entitätsmetadaten zu registrieren.

Verteilte eXtreme-Scale-Konfiguration

Wenn Sie eine verteilte eXtreme-Scale-Konfiguration verwenden, müssen Sie eine Deskriptordatei für die Entitätsmetadaten mit dem Entitätsschema angeben.

Weitere Einzelheiten finden Sie unter EntityManager in einer verteilten Umgebung.

Entitätsvoraussetzungen

Entitätsmetadaten werden mit Java-Klassendateien und/oder einer XML-Entitätsdeskriptordatei konfiguriert. Die Mindestvoraussetzung ist die XML-Entitätdeskriptordatei, die die eXtreme-Scale-BackingMaps identifiziert, die Entitäten zugeordnet werden sollen. Die persistenten Attribute der Entität und die Beziehungen der Entität zu anderen Entitäten werden in einer annotierten Java-Klasse (Entitätsmetadatenklasse) oder in der XML-Entitätsdeskriptordatei beschrieben. Die Entitätsmetadatenklasse, sofern angegeben, wird auch von der API "EntityManager" für die Interaktion mit den Daten im Grid verwendet.

Ein eXtreme-Scale-Grid kann ohne Bereitstellung von Entitätsklassen definiert werden. Dies kann hilfreich sein, wenn der Server und der Client direkt mit den Tupeldaten interagieren, die in den zugrunde liegenden Maps gespeichert sind. Solche Entitäten werden vollständig in der XML-Entitätsdeskriptordatei definiert und als klassenlose Entitäten bezeichnet.

Klassenlose Entitäten

Klassenlose Entitäten sind hilfreich, wenn es nicht möglich ist, Anwendungsklassen in den Server- oder Clientklassenpfad einzuschließen. Solche Entitäten werden in der XML-Deskriptordatei für die Entitätsmetadaten definiert. Der Klassenname der Entität wird mit einer Kennung für klassenlose Entitäten in der Form @<Entitätskennung> angegeben. Das Symbol @ kennzeichnet die Entität als klassenlos und wird für die Zuordnungsassoziationen zwischen Entitäten verwendet. In der Abbildung "Metadaten einer klassenlosen Entität" finden Sie ein Beispiel für eine XML-Deskriptordatei für Entitätsmetadaten mit zwei definierten klassenlosen Entitäten.

Wenn ein eXtreme-Scale-Server oder -Client keinen Zugriff auf die Klassen hat, kann er die API "EntityManager" weiterhin mit klassenlosen Entitäten verwenden. Einige der gängigen Anwendungsfälle sind im Folgenden aufgeführt:

  • Der eXtreme-Scale-Container befindet sich in einem Server, der Anwendungsklassen im Klassenpfad nicht zulässt. In diesem Fall können die Clients weiterhin mit der API "EntityManager" über einen Client zugreifen, in dem die Klassen zulässig sind.
  • Der eXtreme-Scale-Client benötigt keinen Zugriff auf die Entitätsklassen, weil er entweder einen Client verwendet, der kein Java-Client ist, wie z. B. den REST-Datenservice von eXtreme Scale, oder weil er auf die Tupeldaten im Grid mit der API "ObjectMap" zugreift.

Wenn die Entitätsmetadaten von Client und Server kompatibel sind, können Entitätsmetadaten mit Entitätsmetadatenklassen und/oder einer XML-Datei erstellt werden.

Die Klasse "Programmgesteuerte Entitätsklasse" in der folgenden Abbildung ist beispielsweise mit dem klassenlosen Metadatencode im nächsten Abschnitt kompatibel:

Programmgesteuerte Entitätsklasse
@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;
}

Klassenlose Felder, Schlüssel und Versionen

Wie zuvor erwähnt, werden klassenlose Entitäten vollständig in der XML-Entitätsdeskriptordatei konfiguriert. Klassenbasierte Entitäten definieren ihre Attribute mit Java-Feldern, -Eigenschaften und -Annotationen. Deshalb müssen klassenlose Entitäten die Schlüssel- und Attributstruktur im XML-Entitätsdeskriptor mit den Tags <basic> und <id> definieren.

Metadaten einer klassenlosen Entität
<?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>

Beachten Sie, dass jede der vorherigen Entitäten ein Element <id> hat. Für eine klassenlose Entität muss mindestens ein Element <id> oder eine Assoziation mit einem einzelnen Wert definiert werden, die den Schlüssel für die Entität darstellt. Die Felder der Entität werden mit <basic>-Elementen dargestellt. Die Elemente <id>, <version> und <basic> erfordern in klassenlosen Entitäten einen Namen und einen Typ. Einzelheiten zu den unterstützten Typen finden Sie im folgenden Abschnitt zu den unterstützten Attributtypen.

Voraussetzungen für Entitätsklassen

Klassenbasierte Entitäten werden gekennzeichnet, indem verschiedene Metadaten einer Java-Klasse zugeordnet werden. Die Metadaten können mit Annotationen von Java Platform, Standard Edition 5, einer Deskriptordatei für Entitätsmetadaten oder einer Kombination von Annotationen und Deskriptordatei angegeben werden. Entitätsklassen müssen die folgenden Kriterien erfüllen: Entitäten haben alle einen eindeutigen Namen und Typ. Der Name ist bei der Verwendung von Annotationen standardmäßig der einfache (Kurz-)Name der Klasse, der jedoch mit dem Attribut "name" der Annotation @Entity überschrieben werden kann.

Persistente Attribute

Der Zugriff auf den persistenten Zustand einer Entität durch Clients und EntityManager erfolgt über Felder (Instanzvariablen) oder Zugriffsmethoden, die mit Enterprise-JavaBeans-Eigenschaften angegeben werden. Jede Entität muss den feld- oder eigenschaftsbasierten Zugriff definieren. Annotierte Entitäten sind Entitäten mit Feldzugriff, weil die Klassenfelder annotiert sind, und Entitäten mit Eigenschaftszugriff, wenn die Getter-Methode der Eigenschaft annotiert ist. Eine Mischung von Feld- und Eigenschaftszugriff ist nicht zulässig. Wenn der Typ nicht automatisch bestimmt werden kann, kann das Attribut accessType in der Annotation @Entity oder eine funktional entsprechende XML verwendet werden, um den Zugriffstyp zu identifizieren.

Persistente Felder
Instanzvariablen für Entitäten mit Feldzugriff werden direkt über den EntityManager und die Clients aufgerufen. Felder, die mit dem Modifikator oder der Annotation "transient" gekennzeichnet sind, werden ignoriert. Persistente Felder dürfen die Modifikatoren final und static nicht enthalten.
Persistente Eigenschaften
Entitäten mit Eigenschaftenzugriff müssen die JavaBeans-Signaturkonventionen für Lese- und Schreiboperationen einhalten. Methoden, die die JavaBeans-Konventionen nicht einhalten, oder die Annotation "transient" in der Getter-Methode enthalten, werden ignoriert. Für eine Eigenschaft des Typs T muss eine Getter-Methode getProperty vorhanden sein, die den Wert T zurückgibt, und eine Setter-Methode setProperty(T) vom Typ "void". Für boolesche Typen kann die Getter-Methode als isProperty angegeben werden, die "true" oder "false" zurückgibt. Persistente Eigenschaften können keinen statischen Modifikator haben.
Unterstützte Attributtypen
Folgenden Typen werden für persistente Felder und Eigenschaften unterstützt:
  • primitive Java-Typen, einschließlich Wrappern
  • 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
Benutzerdefinierte serialisierbare Attributtypen werden zwar unterstützt, aber für diese gelten Einschränkungen bezüglich der Leistung, Abfrage und Änderungserkennung. Persistente Daten, die nicht über einen Proxy weitergeleitet werden können, wie z. B. Feldgruppen und benutzerdefinierte serialisierbare Objekte, müssen der Entität erneut zugeordnet werden, wenn sie geändert werden.

Serialisierbare Attribute werden in der XML-Entitätsdeskriptordatei mit dem Klassennamen des Objekts dargestellt. Wenn das Objekt eine Feldgruppe (Array) ist, wird der Datentyp im Java-internen Format dargestellt. Wenn ein Attribut den Datentyp java.lang.Byte[][] hat, ist die Zeichenfolgedarstellung [[Ljava.lang.Byte;

Vom Benutzer serialisierbare Typen müssen die folgenden bewährten Verfahren einhalten:

  • Sie müssen Serialisierungsmethoden mit hoher Leistung implementieren. Implementieren Sie die Schnittstelle "java.lang.Cloneable" und die öffentliche Methode "clone".
  • Sie muss die Schnittstelle "java.io.Externalizable" implementieren.
  • Sie muss "equals" und "hashCode" implementieren.

Entitätsassoziationen

Bidirektionale und unidirektionale Entitätsassoziationen oder Beziehungen zwischen Entitäten können als 1:1, n:1, 1:n oder n:n definiert werden. Der Entitätsmanager löst die Entitätsbeziehungen automatisch in die entsprechenden Schlüsselreferenzen auf, wenn die Entitäten gespeichert werden.

Das eXtreme-Scale-Grid ist ein Datencache und erzwingt keine referenzielle Integrität wie eine Datenbank. Obwohl Beziehungen die Operationen "cascading persist" und "remove" für untergeordnete Entitäten zulassen, werden keine fehlerhaften Verknüpfungen mit Objekten erkannt oder umgesetzt. Wenn ein untergeordnetes Objekt entfernt wird, muss die Referenz auf dieses Objekt aus dem übergeordneten Objekt entfernt werden.

Wenn Sie eine bidirektionale Assoziation zwischen zwei Entitäten definieren, müssen Sie den Eigner der Beziehung angeben. In einer :N-Assoziation ist die N-Seite der Beziehung immer der Eigner. Wenn der Eigner nicht automatisch bestimmt werden kann, muss das Attribut mappedBy der Annotation bzw. das funktional entsprechende XML-Element angegeben werden. Das Attribut mappedBy gibt das Feld in der Zielentität an, das Eigner der Beziehung ist. Über diese Attribut können auch die zugehörigen Felder ermittelt werden, wenn es mehrere Attribute mit demselben Typ und derselben Kardinalität gibt.

Assoziation mit einem einzelnen Wert

1:1- und N:1-Assoziationen werden mit den Annotationen "@OneToOne" bzw. "@ManyToOne" oder funktional entsprechenden XML-Attributen gekennzeichnet. Der Typ der Zielentität wird über den Attributtyp bestimmt. Das folgende Beispiel definiert eine unidirektionale Assoziation zwischen Person und Address. Die Entität "Customer" hat eine Referenz auf eine einzige Entität, die Entität "Address". In diesem Fall könnte die Assoziation auch eine n:1-Assoziation sein, das es keine Umkehrbeziehung gibt.
@Entity
public class Customer {
  @Id id;
  @OneToOne Address homeAddress;
}

@Entity
public class Address {
  @Id id
  @Basic String city;
}
Wenn Sie eine bidirektionale Beziehung zwischen den Klassen "Customer" und "Address" angeben möchten, fügen Sie der Klasse "Customer" über die Klasse "Address" eine Referenz hinzu, und fügen Sie anschließend die entsprechende Annotation hinzu, um die Gegenseite der Beziehung zu kennzeichnen. Da diese Assoziation eine 1:1-Assoziation ist, müssen Sie einen Eigner für die Beziehung mit dem Attribut "mappedBy" in der Annotation "@OneToOne" angeben.
@Entity
public class Address {
  @Id id
  @Basic String city;
  @OneToOne(mappedBy="homeAddress") Customer customer;
}

Assoziationen mit Wertesammlungen

1:n- und n:n-Assoziationen werden mit den Annotationen @OneToMany und @ManyToMany oder funktional entsprechenden XML-Attributen gekennzeichnet. Alle Viele-Beziehungen (oder n-Beziehungen) werden mit den Typen java.util.Collection, java.util.List oder java.util.Set dargestellt. Der Typ der Zielentität wird über den generischen Typ des Collection-, List- oder Set-Objekt oder explizit mit dem Attribut targetEntity in der Annotation @OneToMany bzw. @ManyToMany (oder den funktional entsprechenden XML-Elementen) bestimmt.
Im vorherigen Beispiel ist es nicht praktisch, ein einziges Address-Objekt pro Customer-Objekt zu haben, da es viele Kunden geben kann, die dieselbe Adresse oder mehrere Adressen haben können. Diese Situation lässt sich besser über eine Viele-Beziehung lösen:
@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;
}
In diesem Beispiel existieren zwei verschiedene Beziehungen zwischen denselben Entitäten: eine Adressbeziehung "Home" und eine Adressbeziehung "Work". Es wird ein nicht generisches Collection-Objekt für das Attribut workCustomers verwendet, um zu veranschaulichen, wie das Attribut targetEntity verwendet wird, wenn kein generisches Objekt vorhanden ist.

Klassenlose Assoziationen

Klassenlose Entitätsassoziationen werden ähnlich wie klassenbasierte Assoziationen in der XML-Deskriptordatei für die Entitätsmetadaten definiert. Der einzige Unterschied ist der, dass die Zielentität nicht auf eine echte Klasse zeigt, sondern auf die Kennung der klassenlosen Entität, die als Klassenname der Entität verwendet wird.

Es folgt ein Beispiel:

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

Primärschlüssel

Alle Entitäten müssen einen Primärschlüssel haben, der ein einfacher (Einzelattribut) oder ein Verbundschlüssel (mehrere Attribute) sein kann. Die Schlüsselattribute werden mit der Annotation "Id" gekennzeichnet oder in der XML-Deskriptordatei der Entität definiert. Für Schlüsselattribute gelten die folgenden Voraussetzungen: Verbundprimärschlüssel können optional eine Primärschlüsselklasse definieren. Eine Entität wird einer Primärschlüsselklasse mit der Annotation @IdClass oder der XML-Entitätsdeskriptordatei zugeordnet. Eine Annotation @IdClass ist hilfreich, wenn sie zusammen mit der Methode EntityManager.find verwendet wird.
Für Primärschlüsselklassen gelten die folgenden Voraussetzungen:
  • Sie müssen öffentlich sein und einen Konstruktor ohne Argumente haben.
  • Der Zugriffstyp der Primärschlüsselklasse wird über die Entität bestimmt, die die Primärschlüsselklasse deklariert.
  • Wenn der Zugriff über Eigenschaften erfolgt, müssen die Eigenschaften der Primärschlüsselklasse öffentlich oder geschützt sein.
  • Die Primärschlüsselfelder und -eigenschaften müssen den Schlüsselattributnamen und -typen entsprechen, die in der referenzierenden Entität definiert sind.
  • Primärschlüsselklassen müssen die Methoden equals und hashCode implementieren.
Es folgt ein Beispiel:
@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) {...}
}

Klassenlose Primärschlüssel

Klassenlose Entitäten müssen mindestens ein Element <id> oder eine Assoziation in der XML-Datei mit dem Attribut id=true haben. Im Folgenden sehen Sie Beispiele für beide Fälle:

<id name="serialNumber" type="int"/>
<many-to-one name="department" target-entity="@Department" id="true">
	<cascade><cascade-all/></cascade>
</many-to-one>
Hinweis:
Das XML-Tag <id-class> wird für klassenlose Entitäten nicht unterstützt.

Entitäts-Proxys und Feld-Interceptor

Entitätsklassen und veränderliche unterstützte Attributtypen werden durch Proxy-Klassen für Entitäten mit Eigenschaftenzugriff und Bytecode für Entitäten mit Feldzugriff in Java Development Kit (JDK) 5 erweitert. Alle Zugriffe auf die Entität, selbst Zugriffe durch interne Geschäftsmethoden und die equals-Methoden, müssen die entsprechenden Feld- bzw. Eigenschaftenzugriffsmethoden verwenden.

Proxys und Feld-Interceptor werden verwendet, um dem EntityManager zu ermöglichen, den Status der Entität zu verfolgen, zu bestimmen, ob sich die Entität geändert hat und die Leistung zu verbessern. Feld-Interceptor sind nur in Plattformen des Typs Java SE 5 verfügbar, wenn der Instrumentierungsagent für Entitäten konfiguriert ist.

Achtung: Wenn Sie Entitäten mit Eigenschaftenzugriff verwenden, muss die Methode "equals" den Operator "instanceof" verwenden, um die aktuelle Instanz mit dem Eingabeobjekt zu vergleichen. Die gesamte Introspektion des Zielobjekts muss über die Eigenschaften des Objekts und nicht die Felder selbst erfolgen, da die Objektinstanz der Proxy ist.