Uso de un cargador con correlaciones de entidad y tuples

El gestor de entidades convierte todos los objetos de entidad en objetos de tuple 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 asociada de eXtreme Scale para la entidad. Al utilizar una correlación eXtreme Scale con un cargador, éste debe interactuar con los objetos de tuple.

eXtreme Scale contiene plug-ins de cargador que simplifican la integración con las bases de datos relacionales. Los cargadores JPA (Java Persistence) utilizan una Java Persistence API para interactuar con la base de datos y crear los objetos de entidad. Los cargadores JPA son compatibles con las entidades de 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. Después de que se recupere la posición, permanecerá sin cambios durante el ciclo de vida de eXtreme Scale. La posición puede cambiar cuando se reinicie eXtreme Scale. Los métodos setAttribute, setAssociation y setAssociations se utilizan para actualizar los elementos en el tuple.

Atención: Al crear o actualizar los objetos de tuple, actualice todos los campos primitivos con un valor que no sea nulo. Los valores primitivos como, por ejemplo, int no pueden ser nulos. Si no cambia el valor por un valor predeterminado, se pueden generar problemas de rendimiento bajo, que también afectan a los campos marcados con la anotación @Version o el atributo de versión en el archivo XML de descriptor de entidad.

El siguiente ejemplo explica de forma adicional cómo procesar tuples. Para obtener más información sobre cómo definir entidades para este ejemplo, consulte Guía de aprendizaje del gestor de entidades: esquema de entidades Order. WebSphere eXtreme Scale se ha 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 la instancia entityMetaData se ha inicializado durante la llamada al método preLoadMap desde 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 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) {

// El pedido sólo tiene una clave orderNumber
2)                  String ORDERNUMBER=(String) getKeyAttribute("orderNumber", (Tuple) key);
// Obtener el valor de fecha
3)                  java.util.Date unFormattedDate = (java.util.Date) getValueAttribute("date",(Tuple)value);
// Los valores son 2 asociaciones. Permite el proceso de clientes porque
// la tabla contiene customer.id como clave primaria
4)                  Object[] keys= getForeignKeyForValueAssociation("customer","id",(Tuple) value);
                    //Order para Customer es M para 1. Sólo puede haber 1 clave
5)                  String CUSTOMER_ID=(String)keys[0];
// analizar variable unFormattedDate y darle formato para la base de datos como formattedDate
6)                   String formattedDate = "2007-05-08-14.01.59.780272"; // formateado para DB2
// Por último, la sentencia SQL para insertar el registro
7) //INSERT INTO ORDER (ORDERNUMBER, DATE, CUSTOMER_ID) VALUES(ORDERNUMBER,formattedDate, CUSTOMER_ID)
                }
                break;
            case LogElement.CODE_UPDATE:
                break;
            case LogElement.CODE_DELETE:
                break;
            }
        }

    }
// devuelve el valor al atributo según está almacenado en el tuple de clave
 private Object getKeyAttribute(String attr, Tuple key) {
        //obtener metadatos de clave
        TupleMetadata keyMD = entityMetaData.getKeyMetadata();
        //obtener posición del atributo
        int keyAt = keyMD.getAttributePosition(attr);
        if (keyAt > -1) {
            return key.getAttribute(keyAt);
        } else { // attribute undefined
            throw new IllegalArgumentException("Invalid position index for  "+attr);
        }

    }
// devuelve el valor al atributo según está almacenado en el tuple de valor
    private Object getValueAttribute(String attr, Tuple value) {
        //similar a la operación anterior, excepto que se trabaja con metadatos de valor
        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);
        }
    }
// devuelve una matriz de claves que se refiere a la asociación.
    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. 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.
  2. Se procesa KeyTuple y se obtiene el valor del atributo de clave orderNumber
  3. Se procesa ValueTuple y se obtiene el valor de la fecha de atributo
  4. Se procesa ValueTuple y se obtiene el valor de las claves del cliente de asociación
  5. 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.
  6. 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 y el acceso de la transacción a la base de datos se cubre en Escribir un cargador.

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

La clave es un Tuple. El primer paso es convertir el Tuple en valores primitivos que se pueden pasar en la sentencia SELECT de SQL. Después de que se recuperen todos los atributos de la base de datos, debe convertirlos en Tuples. El siguiente código demuestra la clase 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();

// El pedido sólo tiene una clave orderNumber
3)				String ORDERNUMBERKEY = (String) getKeyAttribute("orderNumber",orderKeyTuple);
				//Ejecute una consulta para obtener valores de
4)				// SELECT CUSTOMER_ID, date FROM ORDER WHERE ORDERNUMBER='ORDERNUMBERKEY'

5)				//1) Clave foránea: CUSTOMER_ID
6)				//2) fecha
				// Se presupone que éstos se devuelven como
7)                              String  CUSTOMER_ID = "C001"; // Se presupone recuperación e inicialización
8)				java.util.Date retrievedDate = new java.util.Date(); 
                                // Se presupone que esta fecha refleja la de la base de datos

				// A continuación, se deben convertir estos datos en un tuple antes de devolver

				//crear un tuple de valor
9)				TupleMetadata valueMD = entityMetaData.getValueMetadata();
				Tuple valueTuple=valueMD.createTuple();


				//añadir objeto retrievedDate a Tuple
				int datePosition = valueMD.getAttributePosition("date");
10)				valueTuple.setAttribute(datePosition, retrievedDate);

				//A continuación se debe añadir la asociación
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'
				// Se presupone que dos filas de número de línea se devuelven con los valores 1 y 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 {
			// no admite tuples
		}
		return returnList;
	}
  1. 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.
  2. keyList contiene tuples.
  3. Recupere el valor del atributo orderNumber.
  4. Ejecute la consulta para recuperar la fecha (valor) y el ID de cliente (clave foránea).
  5. CUSTOMER_ID es una clave foránea que se debe establecer en el tuple de asociación.
  6. La fecha es un valor y ya debería estar definido.
  7. Puesto que este ejemplo no realiza llamadas JDBC, se da por supuesto el CUSTOMER_ID.
  8. Dado que este ejemplo no realiza llamadas JDBC, la fecha se da por supuesta.
  9. Cree el valor de Tuple.
  10. Establezca el valor de la fecha en el Tuple, basándose en su posición.
  11. 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.
  12. Encuentre la posición del ID en la entidad del cliente.
  13. Establezca sólo los valores de las claves de asociación.
  14. 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.
  15. Puesto que debe configurar las claves para el lineNumber asociado con este pedido, ejecute SQL para recuperar los valores de lineNumber.
  16. 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 los sucesos del ciclo de vida de transacciones si desea más detalles.