Utilizar un cargador con correlaciones de entidad y tuples
El gestor de entidades convierte todos los objetos de entidad en objetos de tupla antes de que se almacenen en una correlación de WebSphere® eXtreme Scale . Cada entidad tiene un tuple de clave y tuple de valor. Este par de clave-valor se almacena en la correlación de eXtreme Scale asociada para la entidad. Cuando se utiliza una correlación eXtreme Scale con un cargador, el cargador debe interactuar con los objetos de tupla.
eXtreme Scale incluye plug-ins de cargador que simplifican la integración con bases de datos relacionales. Los cargadores JPA (Java™ Persistence API) utilizan una Java Persistence API para interactuar con la base de datos y crear los objetos de entidad. Los cargadores JPA son compatibles con entidades eXtreme Scale .
Tuples
Un tuple contiene información sobre los atributos y las asociaciones de una entidad. Los valores primitivos se almacenan mediante derivadores primitivos. Otros tipos de objeto admitidos se almacenan con su formato nativo. Las asociaciones a otras entidades se almacenan como una colección de objetos de tuples de clave que representan las claves de las entidades de destino.
Cada atributo o asociación se almacena mediante un índice basado en cero. Puede recuperar el índice de cada atributo utilizando los métodos getAttributePosition o getAssociationPosition. Una vez recuperada la posición, permanece sin cambios durante el ciclo de vida de eXtreme Scale . La posición puede cambiar cuando se reinicia eXtreme Scale . Los métodos setAttribute, setAssociation y setAssociations se utilizan para actualizar los elementos en el tuple.
En el ejemplo siguiente se explica más a fondo cómo procesar tuplas. Para obtener más información sobre la definición de entidades para este ejemplo, consulte la Guía de aprendizaje del gestor de entidades: Ordenar esquema de entidad. WebSphere eXtreme Scale está configurado para utilizar cargadores con cada una de las entidades. De forma adicional, sólo se toma la entidad Order y esta entidad específica tiene una relación de muchos a uno con la entidad Customer. El nombre de atributo es customer, y tiene una relación de uno a muchos con la entidad OrderLine.
Utilice Projector para crear automáticamente objetos Tuple de las entidades. La utilización de Projector puede simplificar los cargadores cuando se utiliza un programa de utilidad de correlaciones de objetos relacionales como, por ejemplo, Hibernate o 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;
}
Una clase OrderLoader que implementa la interfaz Loader se muestra en el siguiente código. El siguiente ejemplo presupone que se ha definido un plug-in TransactionCallback asociado.
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 de instancia entityMetaData se inicializa durante elpreLoadMap llamada al método desde el eXtreme Scale. La variable entityMetaData no es nula si la correlación se ha configurado para utilizar entidades. De lo contrario, el valor es nulo.
Método de batchUpdate
El método batchUpdate proporciona la capacidad de saber qué acción tiene previsto realizar la aplicación. Basándose en una operación insertar, actualizar o suprimir, se puede abrir una conexión con la base de datos y el trabajo realizado. Puesto que la clave y los valores son del tipo Tuple, se deben transformar de forma que los valores tengan sentido en la sentencia SQL.
La tabla ORDER se creó con la siguiente definición DLL (lenguaje de definición de datos), tal como se muestra en el código siguiente:
CREATE TABLE ORDER (ORDERNUMBER VARCHAR(250) NOT NULL, DATE TIMESTAMP, CUSTOMER_ID VARCHAR(250))
ALTER TABLE ORDER ADD CONSTRAINT PK_ORDER PRIMARY KEY (ORDERNUMBER)
El código siguiente muestra cómo convertir un tuple en un objeto:
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) {
// The order has just one key orderNumber
2) String ORDERNUMBER=(String) getKeyAttribute("orderNumber", (Tuple) key);
// Get the value of date
3) java.util.Date unFormattedDate = (java.util.Date) getValueAttribute("date",(Tuple)value);
// The values are 2 associations. Lets process customer because
// the our table contains customer.id as primary key
4) Object[] keys= getForeignKeyForValueAssociation("customer","id",(Tuple) value);
//Order to Customer is M to 1. There can only be 1 key
5) String CUSTOMER_ID=(String)keys[0];
// parse variable unFormattedDate and format it for the database as formattedDate
6) String formattedDate = "2007-05-08-14.01.59.780272"; // formatted for DB2
// Finally, the following SQL statement to insert the record
7) //INSERT INTO ORDER (ORDERNUMBER, DATE, CUSTOMER_ID) VALUES(ORDERNUMBER,formattedDate, CUSTOMER_ID)
}
break;
case LogElement.CODE_UPDATE:
break;
case LogElement.CODE_DELETE:
break;
}
}
}
// returns the value to attribute as stored in the key Tuple
private Object getKeyAttribute(String attr, Tuple key) {
//get key metadata
TupleMetadata keyMD = entityMetaData.getKeyMetadata();
//get position of the attribute
int keyAt = keyMD.getAttributePosition(attr);
if (keyAt > -1) {
return key.getAttribute(keyAt);
} else { // attribute undefined
throw new IllegalArgumentException("Invalid position index for "+attr);
}
}
// returns the value to attribute as stored in the value Tuple
private Object getValueAttribute(String attr, Tuple value) {
//similar to previous, except we work with value metadata instead
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);
}
}
// returns an array of keys that refer to 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;
}
- Asegúrese de que entityMetaData no es nulo, lo cual implica que las entradas de memoria caché de clave y valor son del tipo Tuple. En entityMetaData, se recupera la clave TupleMetaData, que refleja sólo la parte de clave de los metadatos Order.
- Se procesa KeyTuple y se obtiene el valor del atributo de clave orderNumber
- Se procesa ValueTuple y se obtiene el valor de la fecha de atributo
- Se procesa ValueTuple y se obtiene el valor de las claves del cliente de asociación
- Se extrae CUSTOMER_ID. Según la relación, un objeto Order sólo puede tener un cliente. Tendremos sólo una clave. Por lo tanto, el tamaño de las claves es 1. Se ha pasado por alto el análisis de la fecha para corregir el formato, para que sea más sencillo.
- Dado que se trata de una operación insert, la sentencia SQL se pasa en la conexión de origen de datos para completar la operación insert.
La demarcación de transacciones y el acceso a la base de datos se tratan en Escritura de un cargador.
Método de get
Si no se encuentra la clave en la memoria caché, llame al método get en el plug-in Loader para encontrar la clave.
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();
// The order has just one key orderNumber
3) String ORDERNUMBERKEY = (String) getKeyAttribute("orderNumber",orderKeyTuple);
//We need to run a query to get values of
4) // SELECT CUSTOMER_ID, date FROM ORDER WHERE ORDERNUMBER='ORDERNUMBERKEY'
5) //1) Foreign key: CUSTOMER_ID
6) //2) date
// Assuming those two are returned as
7) String CUSTOMER_ID = "C001"; // Assuming Retrieved and initialized
8) java.util.Date retrievedDate = new java.util.Date();
// Assuming this date reflects the one in database
// We now need to convert this data into a tuple before returning
//create a value tuple
9) TupleMetadata valueMD = entityMetaData.getValueMetadata();
Tuple valueTuple=valueMD.createTuple();
//add retrievedDate object to Tuple
int datePosition = valueMD.getAttributePosition("date");
10) valueTuple.setAttribute(datePosition, retrievedDate);
//Next need to add the 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'
// Assuming two rows of line number are returned with values 1 and 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 {
// does not support tuples
}
return returnList;
}- Se llama al método get cuando la memoria caché de ObjectGrid no ha podido encontrar la clave y solicita al cargador que la capte. Pruebe el valor de entityMetaData y continué si el valor no es nulo.
- keyList contiene tuples.
- Recupere el valor del atributo orderNumber.
- Ejecute la consulta para recuperar la fecha (valor) y el ID de cliente (clave foránea).
- CUSTOMER_ID es una clave foránea que se debe establecer en el tuple de asociación.
- La fecha es un valor y ya debería estar definido.
- Puesto que este ejemplo no realiza llamadas JDBC, se da por supuesto el CUSTOMER_ID.
- Dado que este ejemplo no realiza llamadas JDBC, la fecha se da por supuesta.
- Cree el valor de Tuple.
- Establezca el valor de la fecha en el Tuple, basándose en su posición.
- Order tiene dos asociaciones. Empiece con el atributo customer que se refiere a la entidad customer. Debe tener el valor del ID para establecerlo en el tuple.
- Encuentre la posición del ID en la entidad del cliente.
- Establezca sólo los valores de las claves de asociación.
- Además, las líneas son una asociación que se debe configurar como un grupo de claves de asociación, de la misma forma que lo haría para la asociación de cliente.
- Puesto que debe configurar las claves para el lineNumber asociado con este pedido, ejecute SQL para recuperar los valores de lineNumber.
- Configure las claves de asociación en el valueTuple. Esto completa la creación del Tuple que se ha devuelto a BackingMap.
En este tema se ofrecen los pasos para crear tuples, y una descripción de la entidad Order solamente. Siga pasos similares para otras entidades, y todo el proceso unido al plug-in TransactionCallback. Consulte Plug-ins para gestionar sucesos de ciclo de vida de transacción para obtener más detalles.