Vous pouvez écrire votre implémentation de plug-in Loader dans vos applications qui doit suivre les conventions de plug-in WebSphere eXtreme Scale communes.
L'interface du chargeur comporte la définition suivante :
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;
}
Pour plus d'informations, voir Chargeurs.
La mappe de sauvegarde appelle la méthode get du chargeur pour obtenir les valeurs associées à une liste de clés transmise en tant qu'argument keyList. La méthode get est requise pour renvoyer une liste de valeurs java.lang.util.List, une valeur pour chaque clé contenue dans la liste de clés. La première valeur renvoyée dans la liste de valeurs correspond à la première clé de la liste de clés, la deuxième valeur renvoyée dans la liste de valeurs correspond à la deuxième clé de la liste de clés et ainsi de suite. Si le chargeur ne trouve pas la valeur pour une clé de la liste de clés, il est requis pour renvoyer l'objet de valeur spécial KEY_NOT_FOUND défini dans l'interface du chargeur. Etant donné qu'une mappe de sauvegarde peut être configurée pour autoriser la valeur NULL comme étant une valeur valide, il est crucial pour le chargeur de renvoyer l'objet spécial KEY_NOT_FOUND lorsque la clé ne peut pas être détectée. Cette valeur spéciale permet à la mappe de sauvegarde de distinguer entre une valeur NULL et une valeur inexistante lorsque la clé est introuvable. Si une mappe de sauvegarde ne prend pas en charge les valeurs NULL, un chargeur renvoyant une valeur NULL et non l'objet KEY_NOT_FOUND pour une clé inexistante déclenche une exception.
L'argument forUpdate informe le chargeur si l'application a appelé une méthode get sur la mappe ou une méthode getForUpdate sur la mappe. Pour plus d'informations, reportez-vous à l' interface ObjectMap. Le chargeur est responsable de l'implémentation d'une stratégie de contrôle des accès simultanés au stockage de persistance. Par exemple, un grand nombre de systèmes de gestion de base de données relationnelle prennent en charge la syntaxe for update sur l'instruction SQL select permettant de lire les données à partir d'une table relationnelle. Le chargeur peut choisir d'utiliser la syntaxe for update sur l'instruction SQL select en fonction de la transmission ou non de la valeur boolean true en que valeur d'argument pour le paramètre forUpdate de cette méthode. Le chargeur utilise généralement la syntaxe for update uniquement lorsque la stratégie de contrôle des accès simultanés pessimiste est mise en oeuvre. Dans le cas d'une stratégie de contrôle des accès simultanés optimiste, le chargeur n'utilise jamais la syntaxe for update sur l'instruction SQL select. Le chargeur décide d'utiliser l'argument forUpdate en fonction de la stratégie de contrôle des accès simultanés mise en oeuvre par le chargeur.
Pour obtenir une explication du paramètre txid, reportez-vous à la section Plug-in de gestion des événements du cycle de vie des transactions.
La méthode batchUpdate est importante dans l'interface Loader. Cette méthode est appelée dès que eXtreme Scale doit appliquer toutes les modifications en cours au chargeur. Le chargeur obtient une liste des modifications pour la mappe sélectionnée. Les modifications sont itérées et appliquées au programme d'arrière plan. La méthode reçoit la valeur TxID et les modifications à appliquer. L'exemple ci-dessous parcourt l'ensemble des modifications et traite par lots trois instructions JDBC (Java Database Connectivity), une avec insert, l'autre avec update et la dernière avec 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 {
// Etablissez une connexion SQL à utiliser.
Connection conn = getConnection(tx);
try {
// Traitez la liste des modifications et créez un ensemble d'instructions
// préparées pour l'exécution d'une opération SQL par lots update, insert
// ou delete.
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;
}
}
// Exécutez les instructions par lots créées par la boucle au-dessus.
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;
}
}
Pendant l'initialisation d'eXtreme Scale , chaque mappe de sauvegarde définie est initialisée. Si un chargeur est relié à une mappe de sauvegarde, cette dernière appelle la méthode preloadMap dans l'interface Loader pour permettre au chargeur de préextraire des données à partir du serveur principal et de les charger dans la mappe. D'après l'exemple suivant, les 100 premières lignes de la table Employee sont lues depuis la base de données et sont chargées dans la mappe. La classe EmployeeRecord est une classe fournie par l'application contenant les données relatives à l'employé lues dans la table employee.
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();
// Etablissez une connexion de validation automatique définie sur
// un niveau d'isolement lecture validée.
conn = getAutoCommitConnection(tx);
// Préchargez la mappe de l'employé avec les objets
// EmployeeRecord. Lisez tous les employés de la table mais
// limitez le préchargement aux 100 premières lignes.
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;
}
// Validez la transaction.
session.commit();
tranActive = false;
} catch (Throwable t) {
throw new LoaderException("preload failure: " + t, t);
} finally {
if (tranActive) {
try {
session.rollback();
} catch (Throwable t2) {
// Tolérez tous les échecs d'annulation et
// autorisez l'émission de la classe throwable originale.
}
}
// Assurez-vous de nettoyer ici les ressources des autres bases de données
// telles que les instructions de clôture, les ensembles de résultats, etc.
}
}
L'exemple preloadMap utilise une instruction SQL Select qui sélectionne toutes les lignes de la table. A partir du chargeur de l'application, il est conseillé de définir une ou plusieurs propriétés du chargeur pour contrôler le degré de préchargement de la table dans la mappe.
Etant donné que la méthode preloadMap n'est appelée qu'une seule fois pendant l'initialisation de la mappe de sauvegarde, il s'agit également d'un bon emplacement pour exécuter le code d'initialisation unique du chargeur. Même si un chargeur choisit de ne pas préextraire de données du serveur principal et de charger les données dans la mappe, il doit probablement procéder à certaines initialisations uniques pour renforcer l'efficacité d'autres méthodes du chargeur. L'exemple suivant illustre la mise en cache des objets TransactionCallback et OptimisticCallback en tant que variables d'instance du chargeur de façon à dispenser les autres méthodes du chargeur d'effectuer des appels de méthode pour accéder à ces objets. Cette mise en cache des valeurs du plug-in ObjectGrid peut être réalisée car les objets TransactionCallback et OptimisticCallback ne peuvent être ni modifiés ni remplacés après l'initialisation de la mappe de sauvegarde. Il est acceptable de mettre en cache ces références d'objet en tant que variables d'instance du chargeur.
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;
// Variables d'instance du chargeur.
MyTransactionCallback ivTcb; // MyTransactionCallback
// étend TransactionCallback
MyOptimisticCallback ivOcb; // MyOptimisticCallback
// implémente OptimisticCallback
// ...
public void preloadMap(Session session, BackingMap backingMap) throws LoaderException
[Replication programming]
// Mettre en cache les objets TransactionCallback et OptimisticCallback
// dans les variables d'instance de ce chargeur.
ivTcb = (MyTransactionCallback) session.getObjectGrid().getTransactionCallback();
ivOcb = (MyOptimisticCallback) backingMap.getOptimisticCallback();
// Le reste du code preloadMap (tel qu'indiqué dans l'exemple précédent).
}
Pour plus d'informations sur le préchargement et le préchargement récupérable dans le cadre du basculement de réplication, voir Réplication à des fins de disponibilitéles informations sur la réplication dans Présentation du produit.
Si le chargeur est relié à une mappe d'entité, il doit traiter des objets de bloc de données. Les objets de bloc de données correspondent à un format de données d'entité spécial. Le chargeur doit convertir les données entre le format de bloc de données et les autres formats de données. Par exemple, la méthode get renvoie une liste de valeurs qui correspond à l'ensemble des clés qui sont transmises à la méthode. Les clés transmises sont de type Bloc de données, à savoir des bloc de données de clés. En supposant que le chargeur conserve la mappe avec une base de données JDBC, la méthode get doit convertir chaque bloc de données de clés en une liste de valeurs d'attribut qui correspondent aux colonnes de la clé primaire de la table mappée sur la mappe d'entité, exécuter l'instruction SELECT avec la clause WHERE qui utilise les valeurs d'attribut converties comme critère d'extraction des données de la base de données, puis convertir les données renvoyées en blocs de données de valeurs. La méthode get extrait les données de la base de données et les convertit en blocs de données de valeurs pour les blocs de données de clés transmis, puis renvoie une liste des blocs de données de valeurs correspondant à l'ensemble des clés de bloc de données transmises au demandeur. La méthode get peut exécuter une instruction SELECT pour extraire toutes les données en même temps ou pour chaque bloc de données de clés. Pour obtenir des détails de programmation illustrant l'utilisation du chargeur lors du stockage des données à l'aide d'un gestionnaire d'entités, reportez-vous à la rubrique Utilisation d'un chargeur avec des mappes d'entité et des tuples.