Sie können eine eigene Loader-Plug-in-Implementierung in Ihren Anwendungen schreiben, die den allgemeinen Konventionen für Plug-ins von WebSphere eXtreme Scale entsprechen muss.
Die Schnittstelle "Loader" hat die folgende Definition:
public interface Loader
{
static final SpecialValue KEY_NOT_FOUND;
List get(TxID txid, List keyList, boolean forUpdate) throws LoaderException;
void batchUpdate(TxID txid, LogSequence sequence) throws LoaderException, OptimisticCollisionException;
void preloadMap(Session session, BackingMap backingMap) throws LoaderException;
}
Weitere Informationen finden Sie im Abschnitt Loader.
Die BackingMap ruft die Methode "get" des Loaders auf, um die Werte abzurufen, die einer Schlüsselliste zugeordnet sind, die als Argument "keyList" übergeben wird. Die Methode "get" ist erforderlich, um eine Werteliste des Typs "java.lang.util.List" für jeden Schlüssel in der Schlüsselliste zurückzugeben. Der erste Wert, der in der Werteliste zurückgegeben wird, entspricht dem ersten Schlüssel in der Schlüsselliste, der zweite zurückgegebene Wert in der Werteliste dem zweiten Schlüssel in der Schlüsselliste usw. Wenn der Loader den Wert für einen Schlüssel in der Schlüsselliste nicht findet, muss der Loader das Sonderwertobjekt KEY_NOT_FOUND zurückgeben, das in der Schnittstelle "Loader" definiert ist. Da eine BackingMap so konfiguriert werden kann, dass sie null als gültigen Wert zulässt, ist es sehr wichtig, dass der Loader das Sonderobjekt KEY_NOT_FOUND zurückgibt, wenn er den Schlüssel nicht finden kann. Anhand dieses Sonderwerts kann die BackingMap zwischen einem Nullwert und einem Wert unterscheiden, der nicht vorhanden ist, weil der Schlüssel nicht gefunden wurde. Wenn eine BackingMap keine Nullwerte unterstützt, führt ein Loader, der einen Nullwert an Stelle des Objekts KEY_NOT_FOUND für einen nicht vorhandenen Schlüssel zurückgibt, zu einer Ausnahme.
Das Argument "forUpdate" teilt dem Loader mit, ob die Anwendung eine Methode "get" für die Map oder eine Methode "getForUpdate" für die Map aufgerufen hat. Weitere Informationen finden Sie in Schnittstelle "ObjectMap". Der Loader ist für die Implementierung einer Richtlinie für die Steuerung des gemeinsamen Zugriffs zuständig, die den gleichzeitigen Zugriff auf den persistenten Speicher steuert. Viele Verwaltungssysteme für relationale Datenbanken unterstützten beispielsweise die Syntax "for update" in der SQL-Anweisung SELECT, die zum Lesen von Daten aus einer relationalen Tabelle verwendet wird. Der Loader kann die Syntax "for update" in der SQL-Anweisung SELECT verwenden, wenn der boolesche Wert true als Argumentwert für den Parameter "forUpdate" dieser Methode übergeben wird. Gewöhnlich verwendet der Loader die Syntax "for update" nur, wenn eine pessimistische Richtlinie für die Steuerung des gemeinsamen Zugriffs verwendet wird. Bei einer optimistischen Steuerung des gemeinsamen Zugriffs verwendet der Loader die Syntax for update nie in der SQL-Anweisung SELECT. Der Loader muss auf der Basis der von ihm verwendeten Richtlinie für die Steuerung des gemeinsamen Zugriffs entscheiden, ob das Argument "forUpdate" verwendet wird.
Eine Erläuterung des Parameters "txid" finden Sie im Abschnitt Plug-ins für die Verwaltung von Ereignissen im Lebenszyklus von Transaktionen.
Die Methode "batchUpdate" ist eine kritische Methode in der Schnittstelle "Loader". Diese Methode wird aufgerufen, wenn eXtreme Scale alle aktuellen Änderungen auf den Loader anwenden muss. Der Loader erhält eine Liste der Änderungen für die ausgewählte Map. Die Änderungen werden iteriert und auf das Back-End angewendet. Die Methode empfängt den aktuellen TxID-Wert und die anzuwendenden Änderungen. Der folgende Beispielcode iteriert durch die Gruppe der Änderungen und führt drei JDBC-Anweisungen (Java Database Connectivity) im Stapelbetrieb aus (eine mit "insert", eine weitere mit "update" und eine weitere mit "delete").
import java.util.Collection;
import java.util.Map;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.ibm.websphere.objectgrid.TxID;
import com.ibm.websphere.objectgrid.plugins.Loader;
import com.ibm.websphere.objectgrid.plugins.LoaderException;
import com.ibm.websphere.objectgrid.plugins.LogElement;
import com.ibm.websphere.objectgrid.plugins.LogSequence;
public void batchUpdate(TxID tx, LogSequence sequence) throws LoaderException {
// Zu verwendende SQL-Verbindung abrufen.
Connection conn = getConnection(tx);
try {
// Liste der Änderungen verarbeiten und eine Gruppe vorbereiteter
// Anweisungen für die Ausführung in einer SQL-Operation update, insert oder delete
// im Stapelbetrieb erstellen.
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:
buildBatchSQLInsert(tx, key, value, conn);
break;
case LogElement.CODE_UPDATE:
buildBatchSQLUpdate(tx, key, value, conn);
break;
case LogElement.CODE_DELETE:
buildBatchSQLDelete(tx, key, conn);
break;
}
}
// Die Stapelanweisungen ausführen, die mit der vorherigen Schleife erstellt wurden.
Collection statements = getPreparedStatementCollection(tx, conn);
iter = statements.iterator();
while (iter.hasNext()) {
PreparedStatement pstmt = (PreparedStatement) iter.next();
pstmt.executeBatch();
}
} catch (SQLException e) {
LoaderException ex = new LoaderException(e);
throw ex;
}
}
Während der Initialisierung von eXtreme Scale wird jede definierte BackingMap initialisiert. Wenn ein Loader in eine BackingMap integriert ist, ruft die BackingMap die Methode "preloadMap" in der Schnittstelle "Loader" auf, damit der Loader Daten vorab aus dem zugehörigen Back-End abrufen und in die Map laden kann. Im folgenden Beispiel wird angenommen, dass die ersten 100 Zeilen einer Tabelle "Employee" aus der Datenbank gelesen und in die Map geladen werden. Die Klasse "EmployeeRecord" ist eine anwendungsdefinierte Klasse, die die Mitarbeiterdaten enthält, die aus der Tabelle "employee" gelesen werden.
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.ibm.websphere.objectgrid.Session;
import com.ibm.websphere.objectgrid.TxID;
import com.ibm.websphere.objectgrid.plugins.Loader;
import com.ibm.websphere.objectgrid.plugins.LoaderException
public void preloadMap(Session session, BackingMap backingMap) throws LoaderException {
boolean tranActive = false;
ResultSet results = null;
Statement stmt = null;
Connection conn = null;
try {
session.beginNoWriteThrough();
tranActive = true;
ObjectMap map = session.getMap(backingMap.getName());
TxID tx = session.getTxID();
// Verbindung mit automatischem Festschreiben abrufen, die
// auf die Isolationsstufe "Lesen mit COMMIT" gesetzt ist.
conn = getAutoCommitConnection(tx);
// EmployeeRecord-Objekte vorher in die Map "Employee"
// laden. Alle Employee-Datensätze aus der Tabelle lesen,
// aber das vorherige Laden auf die ersten 100 Zeilen beschränken.
stmt = conn.createStatement();
results = stmt.executeQuery(SELECT_ALL);
int rows = 0;
while (results.next() && rows < 100) {
int key = results.getInt(EMPNO_INDEX);
EmployeeRecord emp = new EmployeeRecord(key);
emp.setLastName(results.getString(LASTNAME_INDEX));
emp.setFirstName(results.getString(FIRSTNAME_INDEX));
emp.setDepartmentName(results.getString(DEPTNAME_INDEX));
emp.updateSequenceNumber(results.getLong(SEQNO_INDEX));
emp.setManagerNumber(results.getInt(MGRNO_INDEX));
map.put(new Integer(key), emp);
++rows;
}
// Transaktion festschreiben.
session.commit();
tranActive = false;
} catch (Throwable t) {
throw new LoaderException("preload failure: " + t, t);
} finally {
if (tranActive) {
try {
session.rollback();
} catch (Throwable t2) {
// Rollback-Fehler tolerieren und Auslösung
// des ursprünglichen Elements der Throwable-Klasse zulassen.
}
}
// Sicherstellen, dass hier auch andere Datenbankressourcen
// bereinigt werden, z. B. Anweisungen, Ergebnismengen usw. schließen.
}
}
Im Beispiel "preloadMap" wird eine SQL-Anweisung SELECT verwendet, die alle Zeilen der Tabelle auswählt. In Ihrem anwendungsdefinierten Loader müssen Sie möglicherweise eine oder mehrere Loader-Eigenschaften definieren, um zu steuern, wie viele Zeilen der Tabelle vorher in die Map geladen werden müssen.
Da die Methode "preloadMap" nur einmal während der BackingMap-Initialisierung aufgerufen wird, eignet sich sich auch bestens für die einmalige Ausführung des Initialisierungscodes für den Loader. Selbst wenn ein Loader beschließt, Daten nicht vorab aus dem Back-End abzurufen und die Map zu laden, muss er wahrscheinlich irgendeine andere einmalige Initialisierung durchführen, um andere Methoden des Loaders effizienter zu machen. Das folgende Beispiel veranschaulicht das Caching des TransactionCallback-Objekts und des OptimisticCallback-Objekts als Instanzvariablen des Loaders, so dass die anderen Methoden des Loaders keine Methodenaufrufe absetzen müssen, um Zugriff auf diese Objekte zu erhalten. Dieses Caching der ObjectGrid-Plug-in-Werte kann durchgeführt werden, weil die TransactionCallback- und OptimisticCallback-Objekte nach der Initialisierung der BackingMap nicht mehr geändert oder ersetzt werden können. Es ist zulässig, diese Objektreferenzen als Instanzvariablen des Loaders zwischenzuspeichern.
import com.ibm.websphere.objectgrid.Session;
import com.ibm.websphere.objectgrid.BackingMap;
import com.ibm.websphere.objectgrid.plugins.OptimisticCallback;
import com.ibm.websphere.objectgrid.plugins.TransactionCallback;
// Instanzvariablen des Loaders
MyTransactionCallback ivTcb; // MyTransactionCallback
// Erweitert TransactionCallback
MyOptimisticCallback ivOcb; // MyOptimisticCallback
// Implementiert OptimisticCallback
// ...
public void preloadMap(Session session, BackingMap backingMap) throws LoaderException
[Replication programming]
// TransactionCallback- und OptimisticCallback-Objekte
// in Instanzvariablen dieses Loaders zwischenspeichern
ivTcb = (MyTransactionCallback) session.getObjectGrid().getTransactionCallback();
ivOcb = (MyOptimisticCallback) backingMap.getOptimisticCallback();
// Restlicher preloadMap-Code (z. B. wie im vorherigen Beispiel)
}
Weitere Informationen zum Vorabladen und wiederherstellbaren Vorabladen beim Replikationsfailover finden Sie in Replikation für Verfügbarkeitden Informationen zur Replikation in der Veröffentlichung Produktübersicht.
Wenn der Loader in eine Entitäts-Map integriert ist, muss der Loader mit Tupelobjekten arbeiten. Tupelobjekte sind ein spezielles Format von Entitätsdaten. Der Loader muss eine Konvertierung zwischen dem Tupelformat und anderen Datenformaten durchführen. Die Methode "get" gibt beispielsweise eine Liste mit Werten zurück, die der Gruppe von Schlüsseln entspricht, die an die Methode übergeben werden. Die übergebenen Schlüssel haben den Typ "Tupel", d. h., sie sind Schlüsseltupel. Angenommen, der Loader definiert die Map über JDBC in einer Datenbank als persistent. In diesem Fall muss die Methode "get" jedes Schlüsseltupel in eine Liste von Attributwerten konvertieren, die den Primärschlüsselspalten der Tabelle entsprechen, die der Entitäts-Map zugeordnet ist, die Anweisung SELECT mit der WHERE-Klausel ausführen, die konvertierte Attributwerte als Kriterien für den Abruf von Daten aus der Datenbank verwendet, und anschließend die zurückgegebenen Daten in Werttupel konvertieren. Die Methode "get" ruft Daten aus der Datenbank ab, konvertiert die Daten in Werttupel für die übergebenen Schlüsseltupel und gibt dann eine Liste mit Werttupeln zurück, die den Schlüsseltupeln entsprechen, die an den Aufrufenden übergeben werden. Die Methode "get" kann eine einzige Anweisung SELECT ausführen, um alle Daten gleichzeitig abzurufen, oder sie kann für jedes Schlüsseltupel eine eigene Anweisung SELECT ausführen. Ausführliche Informationen zur Programmierung, die zeigen, wie der Loader verwendet wird, wenn die Daten über einen EntityManager gespeichert werden, finden Sie im Abschnitt Loader mit Entitäts-Maps und Tupeln verwenden.