Utilisation d'un chargeur avec des mappes d'entité et des tuples

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.

Tuples

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.

Avertissement : Lorsque vous créez ou mettez à jour des objets tuple, mettez à jour chaque zone primitive avec une valeur non null. Les valeurs primitives telles que int ne doivent pas être égales à null. Si vous ne remplacez pas la valeur par une valeur par défaut, les performances peuvent en être affectées ainsi que les zones identifiées à l'aide de l'annotation @Version ou de l'attribut version dans le fichier XML de descripteur d'entités.

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.

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

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.

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();
	}

}

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.

Méthode batchUpdate

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;

    }
  1. Vérifiez que entityMetaData n'est pas égal à null, ce qui implique que la clé et les entrées de cache de valeur sont de type Tuple. A partir de entityMetaData, Key TupleMetaData est extrait, ce qui reflète uniquement la partie clé des métadonnées Order.
  2. Traitez le KeyTuple et obtenez la valeur de Key Attribute orderNumber
  3. Traitez le ValueTuple et obtenez la valeur de la date d'attribut
  4. Traitez le ValueTuple et obtenez la valeur de Keys from association customer
  5. Extrayez CUSTOMER_ID. En fonction de la relation, une commande peut comporter un seul client, à savoir une seule clé. C'est pourquoi, la taille des clés est de 1. Pour vous simplifer la tâche, nous avons ignoré l'anayse de la date au format correct.
  6. Etant donné qu'il s'agit d'une opération insert, l'instruction SQL est transmise dans la connexion de source de données pour effectuer l'opération insert.

La démarcation de transaction et l'accès à la base de données sont traités dans la rubrique Ecriture d'un chargeur.

Méthode get

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

La clé est un tuple. La première étape à effectuer consiste à convertir le tuple en valeurs primitives pouvant être transmises vers l'instruction SQL SELECT. Une fois que tous les attributs sont extraits de la base de données, vous devez les convertir en tuples. Le code ci-dessous illustre la classe 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();

				// 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;
	}
  1. La méthode get est appelée lorsque le cache ObjectGrid n'a pas pu trouver la clé et demande au chargeur d'extraire. Testez pour la valeur entityMetaData et procédez si non égale à null.
  2. La liste keyList contient des tuples.
  3. Extrayez la valeur de l'attribut orderNumber.
  4. Exécutez la requête pour extraire la date (valeur) et l'ID client (clé externe).
  5. CUSTOMER_ID est une clé externe qui doit être définie dans le tuple d'association.
  6. La date est une valeur et doit être déjà définie.
  7. Etant donné que cet exemple ne met pas en oeuvre des appels JDBC, CUSTOMER_ID est utilisé par défaut.
  8. Etant donné que cet exemple ne met pas en oeuvre des appels JDBC, la date est utilisée par défaut.
  9. Créez la valeur Tuple.
  10. Définissez la valeur de la date dans le tuple en fonction de sa position.
  11. L'entité Order comporte deux associations. Commencez par l'attribut client qui se réfère à l'entité client. Vous devez définir la valeur ID dans le tuple.
  12. Trouvez la position de la valeur ID dans l'entité client.
  13. Définissez les valeurs des clés d'association uniquement.
  14. De même, les lignes constituent des associations qui peuvent être configurées comme groupe de clés d'association, de la même manière que pour l'association client.
  15. Etant donné que vous devez définir les clés pour la lineNumber associée à cette entité Order, exécutez l'instruction SQL pour extraire les valeurs lineNumber.
  16. Configurez les clés d'association dans la valueTuple. La création du tuple renvoyé à la mappe de sauvegarde est ainsi achevée.

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.