Le gestionnaire d'entité convertit tous les objets entité en objets tuple avant qu'ils soient stockés dans une mappe WebSphere eXtreme Scale. Chaque entité contient un tuple de clé et un tuple de valeur. Cette paire clé-valeur est stockée dans la mappe eXtreme Scale associée pour l'entité. Lorsque vous utilisez une mappe eXtreme Scale avec un chargeur, celui-ci doit interagir avec les objets de tuple.
eXtreme Scale comprend des plug-in Loader qui simplifient l'intégration aux bases de données relationnelles. Le chargeur JPA (Java Persistence API) utilise une API de persistance Java pour interagir avec la base de données et créer les objets d'entité. Les chargeurs JPA sont compatibles avec les entités eXtreme Scale.
Un tuple contient des infirmations sur les attributs et les associations d'une entité. Les valeurs primitives sont stockées à l'aide de leurs classes wrapper primitives. D'autres types d'objets pris en charge sont stockés dans leur format natif. Les associations aux autres entités sont stockées sous la forme d'une collection d'objets de tupple de clé représentant les clés des entités cible.
Chaque attribut ou association est stocké à l'aide d'un index de base zéro. Vous pouvez extraire l'index de chaque attribut à l'aide des méthodes getAttributePosition ou getAssociationPosition. Après extraction de la position, celle-ci demeure inchangée pendant toute la durée du cycle de vie d' eXtreme Scale. La position peut changer lorsque eXtreme Scale est redémarré. Les méthodes setAttribute, setAssociation et setAssociations permettent de mettre à jour les éléments du tuple.
L'exemple suivant explique le traitement des tuples. Pour plus d'informations sur la définition des entités pour cet exemple, voir Tutoriel du gestionnaire d'entités : schéma d'entité de commande. WebSphere eXtreme Scale est configuré pour utiliser les chargeurs avec chacune des entités. En outre, seule l'entité Order est extraite et cette entité spécifique présente une relation plusieurs à un avec l'entité Customer. Le nom d'attribut est customer et il présente une relation un à plusieurs avec l'entité OrderLine.
Utilisez le projecteur pour créer des objets Tuple automatiquement à partir des entités. L'utilisation du projecteur peut simplifier celle des chargeurs lorsque vous faites appel à un utilitaire de mappage relationnel objet comme Hibernate ou JPA.
@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;
}
Une classe OrderLoader qui implémente l'interface Loader est illustrée dans le code suivant. L'exemple suivant considère qu'un plug-in TransactionCallback associé est défini.
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();
}
}
La variable d'instance entityMetaData est initialisée pendant l'appel de la méthode preLoadMap à partir du eXtreme Scale. La variable entityMetaData n'est pas égale à null si la mappe est configurée pour utiliser des entités. Dans le cas contraire, la valeur est égale à null.
La méthode batchUpdate offre la fonction permettant de connaître l'action que l'application a voulu effectuer. En fonction d'une opération insert, update ou delete, une connexion peut être établie à la base de données et la tâche effectuée. La clé et les valeurs étant de type Tuple, ils doivent être transformés pour être reconnus par l'instruction SQL.
La tableORDER a été créée avec la définition DDL (Data Definition Language) ci-dessous, tel que l'illustre le code suivant :
CREATE TABLE ORDER (ORDERNUMBER VARCHAR(250) NOT NULL, DATE TIMESTAMP, CUSTOMER_ID VARCHAR(250))
ALTER TABLE ORDER ADD CONSTRAINT PK_ORDER PRIMARY KEY (ORDERNUMBER)
Le code suivant montre comment convertir un tuple dans un objet :
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) {
// La commande comporte un seul orderNumber de clé
2) String ORDERNUMBER=(String) getKeyAttribute("orderNumber", (Tuple) key);
// Obtenir la valeur de la date
3) java.util.Date unFormattedDate = (java.util.Date) getValueAttribute("date",(Tuple)value);
// Les valeurs sont 2 associations. Traitez le client car
// la table our contient customer.id comme clé primaire
4) Object[] keys= getForeignKeyForValueAssociation("customer","id",(Tuple) value);
//Commande client est M à 1. Il ne peut exister qu'1 clé
5) String CUSTOMER_ID=(String)keys[0];
// analysez variable unFormattedDate et formatez-la pour la base de données comme formattedDate
6) String formattedDate = "2007-05-08-14.01.59.780272"; // formaté pour DB2
// Enfin, l'instruction SQL suivante pour insérer l'enregistrement
7) //INSERT INTO ORDER (ORDERNUMBER, DATE, CUSTOMER_ID) VALUES(ORDERNUMBER,formattedDate, CUSTOMER_ID)
}
break;
case LogElement.CODE_UPDATE:
break;
case LogElement.CODE_DELETE:
break;
}
}
}
// renvoie la valeur à l'attribut tel que stocké dans la clé Tuple
private Object getKeyAttribute(String attr, Tuple key) {
//obtenir les métadonnées clé
TupleMetadata keyMD = entityMetaData.getKeyMetadata();
//obtenir la position de l'attribut
int keyAt = keyMD.getAttributePosition(attr);
if (keyAt > -1) {
return key.getAttribute(keyAt);
} else { // attribut non défini
throw new IllegalArgumentException("Invalid position index for "+attr);
}
}
// renvoie la valeur à l'attribut tel que stocké dans la valeur Tuple
private Object getValueAttribute(String attr, Tuple value) {
//semblable à ci-dessus à la différence que nous travaillons avec des métadonnées de valeur
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);
}
}
// renvoie un tableau de clés qui se réfèrent à l'association.
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;
}
La démarcation de transaction et l'accès à la base de données sont traités dans la rubrique Ecriture d'un chargeur.
Si la clé n'est pas trouvée dans le cache, appelez la méthode get dans le plug-in Loader pour trouver la clé.
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();
// La commande comporte un seul orderNumber de clé
3) String ORDERNUMBERKEY = (String) getKeyAttribute("orderNumber",orderKeyTuple);
//Nous devons exécuter une requête pour obtenir les valeurs de
4) // SELECT CUSTOMER_ID, date FROM ORDER WHERE ORDERNUMBER='ORDERNUMBERKEY'
5) //1) Clé externe : CUSTOMER_ID
6) //2) date
// En supposant que les deux sont renvoyés en tant que
7) String CUSTOMER_ID = "C001"; // En prenant en compte les attributs extraits et initialisés
8) java.util.Date retrievedDate = new java.util.Date();
// En supposant que cette date reflète celle de la base de données
// Nous devons désormais convertir ces données en un tuple avant de les renvoyer
//créer un tuple de valeur
9) TupleMetadata valueMD = entityMetaData.getValueMetadata();
Tuple valueTuple=valueMD.createTuple();
//ajouter l'objet retrievedDate au tuple
int datePosition = valueMD.getAttributePosition("date");
10) valueTuple.setAttribute(datePosition, retrievedDate);
//Nous devons ensuite ajouter l'association
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'
// En supposant que deux lignes de numéro de ligne sont renvoyées avec les valeurs 1 et 2
Tuple orderLineKeyTuple1 = orderLineTupleMDForKEY.createTuple();
orderLineKeyTuple1.setAttribute(lineNumberAt, new Integer(1));// set Key
orderLineKeyTuple1.addAssociationKey(orderAt, orderKeyTuple);
Tuple orderLineKeyTuple2 = orderLineTupleMDForKEY.createTuple();
orderLineKeyTuple2.setAttribute(lineNumberAt, new Integer(2));// Init Key
orderLineKeyTuple2.addAssociationKey(orderAt, orderKeyTuple);
16) valueTuple.addAssociationKeys(linesPosition, new Tuple[]
{orderLineKeyTuple1, orderLineKeyTuple2 });
returnList.add(index, valueTuple);
index++;
}
}else {
// ne prend pas en charge les tuples
}
return returnList;
}
Cette rubrique présente les étapes de création des tuples et une description de l'entité Order uniquement. Effectuez les mêmes étapes pour les autres entités et le processus complet lié au plug-in TransactionCallback. Pour plus de détails, reportez-vous à la rubrique Plug-in de gestion des événements du cycle de vie des transactions.