Puede escribir su propia implementación de plug-in de cargador en sus aplicaciones, que debe seguir los convenios de plug-in de WebSphere eXtreme Scale.
La definición de la interfaz Loader es la siguiente:
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;
}
Si desea más información, consulte Cargadores.
La correlación de respaldo llama al método get del cargador para obtener los valores asociados a una lista de claves que se pasa como el argumento keyList. El método get es necesario para devolver una lista java.lang.util.List de valores, un valor para cada clave que aparece en la lista de claves. El primer valor que se devuelve en la lista de valores corresponde a la primera clave de la lista de claves, el segundo valor devuelto en la lista de valores corresponde a la segunda clave de la lista de claves, etc. Si un cargador no encuentra el valor de una clave en la lista de claves, se solicita al cargador que devuelva el objeto de valor especial KEY_NOT_FOUND que se define en la interfaz Loader. Puesto que se puede configurar una correlación de respaldo para permitir null como un valor válido, es muy importante para el cargador devolver el objeto especial KEY_NOT_FOUND cuando el cargador no puede encontrar la clave. Este valor especial permite a la correlación de respaldo distinguir entre un valor nulo y un valor que no existe porque no se encontró. Si una correlación de respaldo no admite valores nulos, se producirá una excepción en un cargador que devuelva un valor nulo en lugar del objeto KEY_NOT_FOUND para una clave que no exista.
El argumento forUpdate indica al cargador si la aplicación llamó a un método get en la correlación o a un método getForUpdate en la correlación. Consulte Interfaz ObjectMap si desea más información. El cargador es responsable de implementar una política de control de simultaneidad que controle los accesos simultáneos al almacén persistente. Por ejemplo, numerosos sistemas de gestión de bases de datos relacionales admiten la sintaxis FOR UPDATE de la sentencia select de SQL que se utiliza para leer los datos de una tabla relacional. El cargador puede elegir utilizar la sintaxis FOR UPDATE en la sentencia select de SQL basándose en si se ha pasado boolean true como el valor del argumento para el parámetro forUpdate de este método. Normalmente, el cargador utilizar la sintaxis FOR UPDATE sólo cuando se utiliza la política de control de simultaneidad pesimista. Para un control de simultaneidad optimista, el cargador nunca utiliza la sintaxis for update en la sentencia select de SQL. El cargador deberá decidir si va a utilizar el argumento forUpdate basado en la política de control de simultaneidad que utiliza el cargador.
Si desea una explicación del parámetro txid, consulte Plug-ins para gestionar los sucesos del ciclo de vida de transacciones.
El método batchUpdate es importante en la interfaz Loader. Este método se llama siempre que eXtreme Scale necesita aplicar todos los cambios actuales al cargador. Se proporciona al cargador una lista de cambios para la correlación seleccionada. Los cambios se repiten y se aplican al programa de fondo. El método recibe el valor TxID actual y los cambios que se aplicarán. El siguiente ejemplo se repite en el conjunto de cambios y procesa por lotes tres sentencias JDBC (Java database connectivity), una con insert, otra con update y una con 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 {
// Obtener una conexión SQL que vaya a utilizarse.
Connection conn = getConnection(tx);
try {
// Procesar la lista de cambios y crear un conjunto de
// sentencias preparadas para ejecutar una
// operación SQL de batch update, insert o 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;
}
}
// Ejecutar las sentencias de proceso por lotes creadas mediante el bucle anterior.
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;
}
}
Durante la inicialización de eXtreme Scale, se inicializa cada correlación de respaldo definida. Si se conecta un cargador en una correlación de respaldo, esta correlación invoca el método preloadMap en la interfaz Loader para permitir al cargador que busque previamente los datos en su programa de fondo y los cargue en la correlación. En el ejemplo siguiente se presupone que las primeras 100 filas de una tabla Employee de empleados se leen de la base de datos y se cargan en la correlación. La clase EmployeeRecord es una clase proporcionada por una aplicación que aloja los datos de empleado que se leen en la tabla de empleados.
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();
// Obtener una conexión de confirmación automática que esté establecida en
// un nivel de aislamiento de lectura confirmada.
conn = getAutoCommitConnection(tx);
// Precargar la correlación de empleados con objetos EmployeeRecord
// . Leer todos los empleados de la tabla, pero
// limitar la precarga a las primeras 100 filas.
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;
}
// Confirmar la transacción.
session.commit();
tranActive = false;
} catch (Throwable t) {
throw new LoaderException("preload failure: " + t, t);
} finally {
if (tranActive) {
try {
session.rollback();
} catch (Throwable t2) {
// Tolerar anomalías de retrotracción y
// permitir que se emitan objetos Throwable originales.
}
}
// Limpie otros recursos de base de datos aquí
// como cierre de sentencias, conjuntos de resultados, etc.
}
}
El ejemplo de preloadMap utiliza una sentencia select de SQL que selecciona todas las filas de la tabla. En el cargador que proporciona la aplicación, puede que necesite establecer una o más propiedades del cargador para controlar cuánta información de la tabla debe precargarse en la correlación.
Como el método preloadMap sólo se llama una vez durante la inicialización de BackingMap, es un buen momento para ejecutar el código de inicialización del cargador de una sola vez. Aunque el cargador decida no buscar previamente los datos en el programa de fondo y cargar los datos en la correlación, probablemente necesite realizar algunas operaciones de inicialización de una sola vez para hacer más eficaces otros métodos del cargador. El ejemplo siguiente ilustra el almacenamiento en memoria caché del objeto TransactionCallback y del objeto OptimisticCallback como variables de instancia del cargador. De esta manera, los otros métodos del cargador no tienen que realizar llamadas de método para obtener acceso a estos objetos. Este almacenamiento en memoria caché de los valores del plug-in ObjectGrid puede realizarse porque, después de que BackingMap se haya inicializado, los objetos TransactionCallback y OptimisticCallback no pueden cambiarse ni sustituirse. Es aceptable almacenar en memoria caché estas referencias de objeto como variables de instancia del cargador.
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 de instancia del cargador.
MyTransactionCallback ivTcb; // MyTransactionCallback
// amplía TransactionCallback
MyOptimisticCallback ivOcb; // MyOptimisticCallback
// implementa OptimisticCallback
// ...
public void preloadMap(Session session, BackingMap backingMap) throws LoaderException
[Programación de réplica]
// Almacenar en memoria caché los objetos TransactionCallback y OptimisticCallback
// en variables de instancia de este cargador.
ivTcb = (MyTransactionCallback) session.getObjectGrid().getTransactionCallback();
ivOcb = (MyOptimisticCallback) backingMap.getOptimisticCallback();
// Resto de código de preloadMap (como se muestra en el ejemplo anterior).
}
Para obtener información sobre la precarga y la precarga recuperable en relación a la migración tras error de réplica, consulte Réplica para la disponibilidadla información sobre la réplica en la Visión general del producto.
Si el cargador se conecta a una correlación de entidad, el cargador debe manejar los objetos de tuple. Los objetos de tuple son un formato especial de datos de entidad. El cargador debe realizar la conversión de datos entre tuple y otros formatos de datos. Por ejemplo, el método get devuelve una lista de valores que corresponden al conjunto de claves que se pasan en el método. La claves que se pasan son de tipo Tuple, es decir, tuples de clave. Si se presupone que el cargador persiste la correlación con una base de datos utilizando JDBC, el método get debe convertir cada tuple de clave en una lista de valores de atributo que corresponden a las columnas de clave primaria de la tabla que se correlaciona con la correlación de entidad, ejecute la sentencia SELECT con la cláusula WHERE que utiliza los valores de atributo convertidos como criterios para captar datos en la base de datos y, a continuación, convertir los datos devueltos en tuples de valor. El método get obtiene datos de la base de datos y los convierte en tuples de valor para los tuples de clave pasados y, a continuación, devuelve una lista de tuples de valor correspondientes al conjunto de claves de tuple que se pasan en al llamante. El método get puede realizar una sentencia SELECT para captar todos los datos a la vez, o ejecutar una sentencia SELECT para cada tuple de clave. Si desea ver detalles de programación que muestran cómo utilizar el cargador cuando se almacenan los datos utilizando un gestor de entidades, consulte Uso de un cargador con correlaciones de entidad y tuples.