Utilizando um Utilitário de Carga com Mapas de Entidade e Tuplas

O gerenciador de entidades converte todos os objetos de entidade em objetos de tupla antes que eles sejam armazenados em um mapa do WebSphere eXtreme Scale. Cada entidade tem uma tupla de chave e uma tupla de valor. Este par chave-valor é armazenado no mapa do eXtreme Scale associado à entidade. Ao usar um mapa do eXtreme Scale com um utilitário de carga, o utilitário de carga deve interagir com os objetos da tupla.

O eXtreme Scale inclui plug-ins do utilitário de carga que simplificam a integração com bancos de dados relacionais. Os Utilitários de Carga da Java Persistence API (JPA) usam uma Java Persistence API para interagir com o banco de dados e criar os objetos de entidade. Os utilitários de carga JPA são compatíveis com as entidades doeXtreme Scale.

Tuplas

Uma tupla contém informações sobre os atributos e associações de uma entidade. Os valores primitivos são armazenados utilizando seus wrappers primitivos. Outros tipos de objeto suportados são armazenados em seu formato nativo. A associações com outras entidades são armazenadas como uma coleta de objetos de tupla de chave que representam as chaves das entidades de destino.

Cada atributo ou associação é armazenado utilizado um índice baseado em zero. É possível recuperar o índice de cada atributo usando os métodos getAttributePosition ou getAssociationPosition. Depois que a posição ser recuperada, ela permanecerá inalterada durante o ciclo de vida do eXtreme Scale. A posição poderá ser alterada quando o eXtreme Scale for reiniciado. Os métodos setAttribute, setAssociation e setAssociations são usados para atualizar os elementos na tupla.

Atenção: Quando você criar ou atualizar objetos da tupla, atualize cada campo de primitiva com um valor não-nulo. Os valores de primitivas como int não devem ser nulos. Se você não alterar o valor para um padrão, poderão ocorrer problemas de desempenho ruim, afetando também campos marcados com a anotação @Version ou atributo de versão no arquivo XML descritor da entidade.

O exemplo a seguir explica como processar as tuplas. Para obter mais informações sobre a definição de entidades para esse exemplo, consulte o Tutorial do Entity Manager: Esquema da Entidade Order. WebSphere eXtreme Scale é configurado para usar os utilitários de carga em cada uma das entidades. Além disso, apenas a entidade Order será usada e esta entidade específica possui um relacionamento muitos-para-um com a entidade Customer. O nome do atributo é customer e ele possui um relacionamento um-para-muitos com a entidade OrderLine.

Utilize o Projector para criar objetos de Tupla automaticamente a partir das entidades. A utilização do Projector simplifica os utilitários de carga ao usar um utilitário de mapeamento relacional de objeto, como 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;
}

Uma classe OrderLoader que implementa a interface do utilitário de carga é mostrada no código a seguir. O seguinte exemplo assume que um plug-in TransactionCallback associado esteja definido.

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

}

A variável da instância entityMetaData é inicializada durante a chamada de método preLoadMap a partir do eXtreme Scale. A variável entityMetaData não será nula se o Mapa for configurado para usar entidades. Caso contrário, o valor será nulo.

Método batchUpdate

O método batchUpdate fornece a habilidade de saber que ação o aplicativo pretende executar. Com base em uma operação de inserção, atualização ou exclusão, uma conexão pode ser aberta com o banco de dados e o trabalho ser executado. Como a chave e os valores são do tipo Tupla, eles devem ser transformados para que os valores façam sentido na instrução SQL.

A tabela ORDER foi criada com a definição de DDL (Data Definition Language) mostrada no código a seguir:

CREATE TABLE ORDER (ORDERNUMBER VARCHAR(250) NOT NULL, DATE TIMESTAMP, CUSTOMER_ID VARCHAR(250))
ALTER TABLE ORDER ADD CONSTRAINT PK_ORDER PRIMARY KEY (ORDERNUMBER)

O código a seguir demonstra como converter uma Tupla em um 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 above, 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;

    }
  1. Certifique-se de que entityMetaData não seja nulo, o que implica na entradas do cache de chave e valor serem do tipo Tuple. A partir de entityMetaData, Key TupleMetaData é recuperado, o que realmente reflete apenas a parte principal dos metadados Order.
  2. Processe o KeyTuple e obtenha o valor do Atributo-chave orderNumber
  3. Processe o ValueTuple e obtenha o valor da data do atributo
  4. Processe o ValueTuple e obtenha o valor das chaves do consumidor da associação
  5. Extraia CUSTOMER_ID. Com base no relacionamento, uma ordem pode ter apenas um consumidor; portanto, teremos apenas uma chave. Por isso, o tamanho das chaves é 1. Por simplicidade, ignoramos a análise da data para verificar se o formato está correto.
  6. Como esta é uma operação insert, a instrução SQL é transmitida para a conexão da origem de dados para concluir a operação insert.

A demarcação da transação e o acesso ao banco de dados são abordados em Criando um Utilitário de Carga.

Método get

Se a chave não for localizada no cache, chame o método get no plug-in do Utilitário de Carga para localizar a chave.

A chave é uma Tupla. A primeira etapa é converter a Tupla para valores primitivos que possam ser transmitidos na instrução SELECT SQL. Depois de todos os atributos serem recuperados do banco de dados, converta-os em Tuplas. O código a seguir demonstra a 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();

				// 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;
	}
  1. O método get é chamado quando o cache do ObjectGrid não consegue localizar a chave e solicita que o utilitário de carga faça a busca. Teste o valor para entityMetaData e continue se ele não for nulo.
  2. A keyList contém Tuplas.
  3. Recupere o valor de atributo orderNumber.
  4. Execute a consulta para recuperar a data (valor) e o ID do cliente (chave estrangeira).
  5. CUSTOMER_ID é uma chave estrangeira que deve ser configurada na tupla de associação.
  6. A data é um valor e já deverá estar configurada.
  7. Como este exemplo não executa chamadas JDBC, CUSTOMER_ID é assumido.
  8. Como este exemplo não executa chamadas JDBC, a data é assumida.
  9. Crie a Tupla de valor.
  10. Configure o valor da data na Tupla, com base em sua posição.
  11. O pedido possui duas associações. Inicie com o atributo customer que faz referência à entidade do cliente. Você deve ter o valor do ID para configurar na Tupla.
  12. Localize a posição do ID na entidade do cliente.
  13. Configure os valores apenas das chaves de associação.
  14. Além disso, as linhas são uma associação que devem ser configuradas como grupo de chaves de associação, da mesma forma como é feito para a associação do cliente.
  15. Como é necessário configurar as chaves para o lineNumber associado a este pedido, execute o SQL para recuperar os valores de lineNumber.
  16. Configure as chaves de associação no valueTuple. Isto conclui a criação da Tupla que é retornada ao BackingMap

Este tópico oferece as etapas para criação de tuplas, e uma descrição apenas para a entidade Order. Execute etapas semelhantes para as outras entidades e todo o processo que está ligado ao plug-in TransactionCallback. Consulte Plug-ins para o Gerenciamento de Eventos de Ciclo de Vida da Transação para obter detalhes.