Loader mit Entitäts-Maps und Tupeln verwenden

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.

Tupel

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.

Achtung: Wenn Sie Tupelobjekte erstellen oder aktualisieren, müssen Sie jedes primitive Feld mit einem Wert ungleich null aktualisieren. Primitive Werte wie int dürfen nicht null sein. Wenn sie den Wert nicht in einen Standardwert ändern, treten Leistungsprobleme auf, die sich auch auf Felder auswirken, die mit der Annotation "@Version" oder einem Versionsattribut in der XML-Entitätsdeskriptordatei markiert sind.

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.

order.java

@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;
}

customer.java

@Entity
public class Customer {
    @Id String id;
    String firstName;
    String surname;
    String address;
    String phoneNumber;
}

orderLine.java

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

orderLoader.java

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.

Methode "batchUpdate"

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;

    }
  1. Stellen Sie sicher, dass entityMetaData nicht null ist, was impliziert, dass die Cacheeinträge für Schlüssel und Wert Tupel sind. Aus den entityMetaData werden nur die TupleMetaData für den Schlüssel abgerufen, die wirklich nur den Schlüsselteil der Auftragsmetadaten enthalten.
  2. Verarbeiten Sie das Schlüsseltupel (KeyTuple), und rufen Sie den Wert des Schlüsselattributs "orderNumber" ab.
  3. Verarbeiten Sie das Werttupel (ValueTuple), und rufen Sie den Wert des Attributs "date" ab.
  4. Verarbeiten Sie das Werttupel (ValueTuple), und rufen Sie den Wert der Schlüssel aus der Assoziation "customer" ab.
  5. Extrahieren Sie CUSTOMER_ID. Basierend auf der Beziehung kann ein Auftrag (Order) nur einen einzigen Kunden (Customer) haben. Deshalb gibt es auch nur einen einzigen Schlüssel. Die Größe der Schlüssel ist damit 1. Zur Einfachheit wird die Syntaxanalyse des Datums auf das richtige Format übersprungen.
  6. Da es sich um eine Einfügeoperation (insert) handelt, wird die SQL-Anweisung in der Datenquellenverbindung übergeben, um die Einfügeoperation durchzuführen.

Informationen zur Transaktionsabgrenzung und zum Zugriff auf die Datenbank finden Sie im Abschnitt Loader schreiben.

Methode "get"

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.

Der Schlüssel ist ein Tupel. Der erste Schritt ist die Konvertierung des Tupels in primitive Werte, die an die SQL-Anweisung SELECT übergeben werden können. Nachdem alle Attribute aus der Datenbank abgerufen wurden, müssen Sie sie in Tupel konvertieren. Der folgende Code demonstriert die Klasse "Order":
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;
	}
  1. Die Methode "get" wird aufgerufen, wenn der ObjectGrid-Cache den Schlüssel nicht findet und den Loader auffordert, den Schlüssel abzurufen. Suchen Sie den entityMetaData-Wert, und fahren Sie fort, wenn der Wert ungleich null ist.
  2. Die Schlüsselliste enthält Tupel.
  3. Rufen Sie den Wert des Attributs "orderNumber" ab.
  4. Führen Sie die Abfrage aus, um das Datum (Wert) und die Kunden-ID (Fremdschlüssel) abzurufen.
  5. CUSTOMER_ID ist ein Fremdschlüssel, der im Assoziationstupel gesetzt werden muss.
  6. Das Datum ist ein Wert und muss bereits gesetzt sein.
  7. Da in diesem Beispiel keine JDBC-Aufrufe ausgeführt werden, wird CUSTOMER_ID angenommen.
  8. Da in diesem Beispiel keine JDBC-Aufrufe ausgeführt werden, wird date angenommen.
  9. Erstellen Sie das Werttupel.
  10. Setzen Sie den Wert von "date" im Tupel, je nach Position.
  11. Das Order-Objekt hat zwei Assoziationen. Beginnen Sie mit dem Attribut "customer", das auf die Kundenentität verweist. Sie müssen den Wert von ID haben, um ihn im Tupel zu setzen.
  12. Ermitteln Sie die Position von ID in der Entity "Customer".
  13. Setzen Sie nur die Werte der Assozationsschlüssel.
  14. "lines" ist auch eine Assoziation, die als Gruppe von Assoziationsschlüsseln konfiguriert werden muss (auf dieselbe Weise wie bei der customer-Assoziation).
  15. Da Sie Schlüssel für die Positionsnummer (lineNumber) festlegen müssen, die diesem Auftrag zugeordnet ist, führen Sie die SQL zum Abrufen der lineNumber-Werte aus.
  16. Definieren Sie die Assoziationsschlüssel im Werttupel (valueTuple). Diese Aktion schließt die Erstellung des Tupels ab, das an die BackingMap zurückgegeben wird.

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.