Der EntityManager konvertiert alle Entitätsobjekte in Tupelobjekte, bevor sie in einer eXtreme-Scale-Map gespeichert werden. Jede Entität hat ein Schlüsseltupel und ein Werttupel. Dieses Schlüssel/Wert-Paar wird in der eXtreme-Scale-Map gespeichert, die der Entität zugeordnet ist. Wenn Sie eine eXtreme-Scale-Map mit einem Loader verwenden, muss der Loader mit den Tupelobjekten interagieren.
eXtreme Scale enthält Loader-Plug-ins, die die Integration mit relationalen Datenbanken vereinfachen. Die JPA-Loader (Java Persistence API) verwenden eine Java Persistence API, um mit der Datenbank zu interagieren und die Entitätsobjekte zu erstellen. Die JPA-Loader sind mit eXtreme-Scale-Entitäten kompatibel.
Ein Tupel enthält Informationen zu den Attributen und Assoziationen einer Entität. Primitive Werte werden über ihre primitiven Wrapper gespeichert. Andere unterstützte Objekttypen werden in ihrem nativen Format gespeichert. Assoziationen zu anderen Entitäten werden als Sammlung von Schlüsseltupelobjekten gespeichert, die die Schlüssel der Zielentitäten darstellen.
Jedes Attribut und jede Assoziation wird über einen nullbasierten Index gespeichert. Sie können den Index jedes Attributs mit der Methode "getAttributePosition" oder "getAssociationPosition" abrufen. Nachdem Sie die Position abgerufen haben, bleibt diese für die Dauer des Lebenzyklus von eXtreme Scale unverändert. Die Position kann sich ändern, wenn eXtreme Scale erneut gestartet wird. Die Methoden "setAttribute", "setAssociation" und "setAssociations" werden verwendet, um die Elemente im Tupel zu aktualisieren.
Im folgenden Beispiel wird ausführlicher erläutert, wie Tupel verarbeitet werden. Weitere Informationen zum Definieren von Entitäten für dieses Beispiel finden Sie im Abschnitt Lernprogramm zum EntityManager: Schema für die Entität "Order". WebSphere eXtreme Scale ist so konfiguriert, dass für jede Entität ein Loader verwendet wird. Außerdem wird nur die Entität "Order" verwendet, und diese Entität hat eine Viele-zu-eins-Beziehung zur Entität "Customer". Der Attributname ist customer, und dieses Attribut hat eine Eins-zu-viele-Beziehung zur Entität "OrderLine".
Verwenden Sie den Projektor, um Tupelobjekte automatisch aus Entitäten zu erstellen. Die Verwendung des Projektors kann Loader vereinfachen, wenn ein ORM-Dienstprogramm (Object-Relational Mapping, objektrelationale Abbildung) wie Hibernate oder JPA verwendet wird.
@Entity
public class Order
{
@Id String orderNumber;
java.util.Date date;
@OneToOne(cascade=CascadeType.PERSIST) Customer customer;
@OneToMany(cascade=CascadeType.ALL, mappedBy="order") @OrderBy("lineNumber") List<OrderLine> lines;
}
@Entity
public class Customer {
@Id String id;
String firstName;
String surname;
String address;
String phoneNumber;
}
@Entity
public class OrderLine
{
@Id @ManyToOne(cascade=CascadeType.PERSIST) Order order;
@Id int lineNumber;
@OneToOne(cascade=CascadeType.PERSIST) Item item;
int quantity;
double price;
}
Eine Klasse "OrderLoader", die die Schnittstelle "Loader" implementiert, wird im folgenden Code gezeigt. Im folgenden Beispiel wird angenommen, dass ein zugehöriges TransactionCallback-Plug-in definiert ist.
public class OrderLoader implements com.ibm.websphere.objectgrid.plugins.Loader {
private EntityMetadata entityMetaData;
public void batchUpdate(TxID txid, LogSequence sequence)
throws LoaderException, OptimisticCollisionException {
...
}
public List get(TxID txid, List keyList, boolean forUpdate)
throws LoaderException {
...
}
public void preloadMap(Session session, BackingMap backingMap)
throws LoaderException {
this.entityMetaData=backingMap.getEntityMetadata();
}
}
Die Instanzvariable "entityMetaData" wird während des Aufrufs der Methode "preLoadMap" über eXtreme Scale initialisiert. Die Variable entityMetaData ist nicht null, wenn die Map für die Verwendung von Entitäten konfiguriert ist. Andernfalls ist der Wert null.
Mit der Methode "batchUpdate" kann festgestellt werden, welche Aktion die Anwendung ausführen wollte. Auf der Basis einer Operation "insert", "update" oder "delete" kann eine Verbindung zur Datenbank geöffnet und die arbeit ausgeführt werden. Da der Schlüssel und die Werte vom Typ "Tupel" sind, müssen sie umgesetzt werden, so dass sie in der SQL-Anweisung Sinn machen.
Die Tabelle ORDER wurde mit der DDL-Definition (Data Definition Language) erstellt, die Sie im folgenden Code sehen:
CREATE TABLE ORDER (ORDERNUMBER VARCHAR(250) NOT NULL, DATE TIMESTAMP, CUSTOMER_ID VARCHAR(250))
ALTER TABLE ORDER ADD CONSTRAINT PK_ORDER PRIMARY KEY (ORDERNUMBER)
Der folgende Code veranschaulicht wie Sie ein Tupel in ein Objekt konvertieren:
public void batchUpdate(TxID txid, LogSequence sequence)
throws LoaderException, OptimisticCollisionException {
Iterator iter = sequence.getPendingChanges();
while (iter.hasNext()) {
LogElement logElement = (LogElement) iter.next();
Object key = logElement.getKey();
Object value = logElement.getCurrentValue();
switch (logElement.getType().getCode()) {
case LogElement.CODE_INSERT:
1) if (entityMetaData!=null) {
// Der Auftrag (order) hat nur einen einzigen Schlüssel, die Auftragsnummer (orderNumber).
2) String ORDERNUMBER=(String) getKeyAttribute("orderNumber", (Tuple) key);
// Wert von date abrufen.
3) java.util.Date unFormattedDate = (java.util.Date) getValueAttribute("date",(Tuple)value);
// Die Werte sind 2 Assoziationen. Kunden verarbeiten, weil die Tabelle
// customer.id als Primärschlüssel enthält.
4) Object[] keys= getForeignKeyForValueAssociation("customer","id",(Tuple) value);
//Beziehung zwischen Order und Customer ist M zu 1. Es kann nur einen einzigen Schlüssel geben.
5) String CUSTOMER_ID=(String)keys[0];
// Variable unFormattedDate syntaktisch analysieren und für die Datenbank als formattedDate formatieren
6) String formattedDate = "2007-05-08-14.01.59.780272"; // formatted for DB2
// Abschließend die folgende SQL-Anweisung, um den Datensatz einzufügen
7) //INSERT INTO ORDER (ORDERNUMBER, DATE, CUSTOMER_ID) VALUES(ORDERNUMBER,formattedDate, CUSTOMER_ID)
}
break;
case LogElement.CODE_UPDATE:
break;
case LogElement.CODE_DELETE:
break;
}
}
}
// Gibt den Wert für das Attribut zurück, der im Schlüsseltupel gespeichert ist
private Object getKeyAttribute(String attr, Tuple key) {
// Metadaten des Schlüssels abrufen
TupleMetadata keyMD = entityMetaData.getKeyMetadata();
// Position des Attributs abrufen
int keyAt = keyMD.getAttributePosition(attr);
if (keyAt > -1) {
return key.getAttribute(keyAt);
} else { // attribute undefined
throw new IllegalArgumentException("Invalid position index for "+attr);
}
}
// Gibt den Wert für das Attribut zurück, der im Werttupel gespeichert ist
private Object getValueAttribute(String attr, Tuple value) {
// Ähnlich wie oben, abgesehen davon, dass mit den Metadaten des Werts gearbeitet wird
TupleMetadata valueMD = entityMetaData.getValueMetadata();
int keyAt = valueMD.getAttributePosition(attr);
if (keyAt > -1) {
return value.getAttribute(keyAt);
} else {
throw new IllegalArgumentException("Invalid position index for "+attr);
}
}
// Gibt einen Bereich von Schlüsseln zurück, die auf die Assoziation verweisen
private Object[] getForeignKeyForValueAssociation(String attr, String fk_attr, Tuple value) {
TupleMetadata valueMD = entityMetaData.getValueMetadata();
Object[] ro;
int customerAssociation = valueMD.getAssociationPosition(attr);
TupleAssociation tupleAssociation = valueMD.getAssociation(customerAssociation);
EntityMetadata targetEntityMetaData = tupleAssociation.getTargetEntityMetadata();
Tuple[] customerKeyTuple = ((Tuple) value).getAssociations(customerAssociation);
int numberOfKeys = customerKeyTuple.length;
ro = new Object[numberOfKeys];
TupleMetadata keyMD = targetEntityMetaData.getKeyMetadata();
int keyAt = keyMD.getAttributePosition(fk_attr);
if (keyAt < 0) {
throw new IllegalArgumentException("Invalid position index for " + attr);
}
for (int i = 0; i < numberOfKeys; ++i) {
ro[i] = customerKeyTuple[i].getAttribute(keyAt);
}
return ro;
}
Informationen zur Transaktionsabgrenzung und zum Zugriff auf die Datenbank finden Sie im Abschnitt Loader schreiben.
Wenn der Schlüssel nicht im Cache gefunden wird, rufen Sie die Methode "get" im Loader-Plug-in auf, um den Schlüssel zu suchen.
public List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException {
System.out.println("OrderLoader: Get called");
ArrayList returnList = new ArrayList();
1) if (entityMetaData != null) {
int index=0;
for (Iterator iter = keyList.iterator(); iter.hasNext();) {
2) Tuple orderKeyTuple=(Tuple) iter.next();
// Der Auftrag (order) hat nur einen einzigen Schlüssel, die Auftragsnummer (orderNumber).
3) String ORDERNUMBERKEY = (String) getKeyAttribute("orderNumber",orderKeyTuple);
//Es muss eine Abfrage ausgeführt werden, um die Werte von
4) // SELECT CUSTOMER_ID, date FROM ORDER WHERE ORDERNUMBER='ORDERNUMBERKEY' abzurufen
5) //1) Fremdschlüssel: CUSTOMER_ID
6) //2) date
// Annehmen, dass diese beiden wie folgt zurückgegeben werden:ls
7) String CUSTOMER_ID = "C001"; // Annehmen, dass sie abrufen und initialisiert wurden
8) java.util.Date retrievedDate = new java.util.Date();
// Annehmen, dass dieses Datum das Datum in der Datenbank widerspiegelt
// Jetzt müssen diese Daten vor der Rückgabe in eine Tupel konvertiert werden.
// Werttupel erstellen
9) TupleMetadata valueMD = entityMetaData.getValueMetadata();
Tuple valueTuple=valueMD.createTuple();
// retrievedDate-Objekt dem Tupel hinzufügen
int datePosition = valueMD.getAttributePosition("date");
10) valueTuple.setAttribute(datePosition, retrievedDate);
// Jetzt muss die Assoziation hinzugefügt werden.
11) int customerPosition=valueMD.getAssociationPosition("customer");
TupleAssociation customerTupleAssociation =
valueMD.getAssociation(customerPosition);
EntityMetadata customerEMD = customerTupleAssociation.getTargetEntityMetadata();
TupleMetadata customerTupleMDForKEY=customerEMD.getKeyMetadata();
12) int customerKeyAt=customerTupleMDForKEY.getAttributePosition("id");
Tuple customerKeyTuple=customerTupleMDForKEY.createTuple();
customerKeyTuple.setAttribute(customerKeyAt, CUSTOMER_ID);
13) valueTuple.addAssociationKeys(customerPosition, new Tuple[] {customerKeyTuple});
14) int linesPosition = valueMD.getAssociationPosition("lines");
TupleAssociation linesTupleAssociation = valueMD.getAssociation(linesPosition);
EntityMetadata orderLineEMD = linesTupleAssociation.getTargetEntityMetadata();
TupleMetadata orderLineTupleMDForKEY = orderLineEMD.getKeyMetadata();
int lineNumberAt = orderLineTupleMDForKEY.getAttributePosition("lineNumber");
int orderAt = orderLineTupleMDForKEY.getAssociationPosition("order");
if (lineNumberAt < 0 || orderAt < 0) {
throw new IllegalArgumentException(
"Invalid position index for lineNumber or order "+
lineNumberAt + " " + orderAt);
}
15) // SELECT LINENUMBER FROM ORDERLINE WHERE ORDERNUMBER='ORDERNUMBERKEY'
// Annehmen, dass zwei linenumber-Zeilen mit den Werten 1 und 2 zurückgegeben werden
Tuple orderLineKeyTuple1 = orderLineTupleMDForKEY.createTuple();
orderLineKeyTuple1.setAttribute(lineNumberAt, new Integer(1));// Schlüssel setzen
orderLineKeyTuple1.addAssociationKey(orderAt, orderKeyTuple);
Tuple orderLineKeyTuple2 = orderLineTupleMDForKEY.createTuple();
orderLineKeyTuple2.setAttribute(lineNumberAt, new Integer(2));// Schlüssel initialisieren
orderLineKeyTuple2.addAssociationKey(orderAt, orderKeyTuple);
16) valueTuple.addAssociationKeys(linesPosition, new Tuple[]
{orderLineKeyTuple1, orderLineKeyTuple2 });
returnList.add(index, valueTuple);
index++;
}
}else {
// Unterstützt keine Tupel
}
return returnList;
}
Dieser Abschnitt beschreibt die Schritte zum Erstellen von Tupeln und enthält nur eine Beschreibung der Entität "Order". Führen Sie ähnliche Schritte für die anderen Entitäten und den gesamten Prozess aus, der in Zusammenhang mit dem TransactionCallback-Plug-in steht. Weitere Einzelheiten finden Sie im Abschnitt Plug-ins für die Verwaltung von Ereignissen im Lebenszyklus von Transaktionen.