Definindo um Esquema de Entidade

Um ObjectGrid pode ter inúmeros esquemas de entidade lógicos. As entidades são definidas usando as classes Java anotadas, o XML ou uma combinação de classes XML e Java. Entidades definidas são registradas com um servidor eXtreme Scale e ligadas a BackingMaps, índices e outros plug-ins.

Ao projetar uma esquema de entidade, é necessário concluir as seguintes tarefas:
  1. Definir as entidades e seus relacionamentos.
  2. Configurar o eXtreme Scale.
  3. Registrar as entidades.
  4. Criar aplicativos baseados em entidade que interajam com as APIs EntityManager do eXtreme Scale.

Configuração do Esquema de Entidade

Um esquema de entidade é um conjunto de entidades e relacionamentos entre as entidades. Em um aplicativo eXtreme Scale com várias partições, as opções e restrições a seguir aplicam-se aos esquemas de entidade: Entidades são registradas com uma instância de ObjectGrid antes de serem inicializadas. Cada entidade definida deve ser nomeada exclusivamente e ligada automaticamente a um BackingMap de ObjectGrid de mesmo nome. O método de inicialização varia dependendo da configuração que você utilizando:

Configuração do eXtreme Scale local

Se estiver utilizando um ObjectGrid local, será possível configurar programaticamente o esquema de entidade. Neste modo, é possível utilizar os métodos ObjectGrid.registerEntities para registrar classes de entidade anotadas ou um arquivo descritor de metadados.

Configuração do eXtreme Scale distribuída

Se você estiver utilizando uma configuração do eXtreme Scale distribuída, é necessário fornecer um arquivo descritor de metadados da entidade com o esquema de entidade.

Para obter detalhes adicionais, consulte Entity Manager em um Ambiente Distribuído.

Requisitos de Entidade

Os metadados de entidade são configurados com o uso de arquivos de classe Java, um XML do descritor de entidade ou ambos. No mínimo, o XML do descritor de entidade é requerido para identificar quais BackingMaps do eXtreme Scale devem ser associados às entidades. Os atributos persistentes da entidade e seus relacionamentos com outras entidades são descritos em uma classe Java anotada (classe de metadados da entidade) ou arquivo XML do descritor de entidade. A classe de metadados da entidade, quando especificada, também é utilizada pela API EntityManager para interagir com dados na grade.

Uma grade do eXtreme Scale pode ser definida sem fornecer quaisquer classes de entidade. Isso pode ser benéfico quando o servidor e o cliente estiverem interagindo diretamente com os dados da tupla armazenados nos mapas subjacentes. Tais entidades são definidas completamente no arquivo XML do descritor de entidade e são referidas como entidades sem classe.

Entidades sem Classe

As entidades sem classe são úteis quando não é possível incluir classes de aplicativo no caminho de classe do servidor ou do cliente. Tais entidades são definidas no arquivo XML do descritor de entidade, onde o nome da classe da entidade é especificado utilizando um identificador de entidade sem classe no formato: @<identificador de entidade>. O símbolo @ identifica a entidade como sem classe e é utilizado para mapear associações entre entidades. Consulte a figura "Metadados da Entidade sem Classe" para ver um exemplo de um arquivo XML do descritor de metadados da entidade com duas entidades sem classe definidas.

Se um cliente ou servidor eXtreme Scale não tiver acesso às classes, eles poderão utilizar a API EntityManager utilizando as entidades sem classes. Casos de uso comuns incluem o seguinte:

  • O contêiner do eXtreme Scale é hospedado em um servidor que não permite classes de aplicativo no caminho de classe. Nesse caso, os clientes ainda podem acessar a grade utilizando a API EntityManager de um cliente em que as classes sejam permitidas.
  • O cliente eXtreme Scale não requer acesso às classes de entidade porque o cliente está utilizando um cliente não Java, como o serviço de dados REST do eXtreme Scale, ou o cliente está acessando os dados da tupla na grade utilizando a API de ObjectMap.

Se os metadados da entidade forem compatíveis entre cliente e servidor, eles poderão ser criados utilizando classes de metadados da entidade, um arquivo XML ou ambos.

Por exemplo, a "Classe de Entidade Programática" na figura a seguir é compatível com o código de metadados sem classe na próxima seção.

Classe de entidade programática
@Entity
public class Employee {
    @Id long serialNumber;
    @Basic byte[] picture;
    @Version int ver;
    @ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.PERSIST)
    Department department;
}

@Entity
public static class Department {
    @Id int number;
    @Basic String name;
    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="department")
    Collection<Employee> employees;
}

Versões, Chaves e Campos sem Classe

Conforme mencionado anteriormente, as entidades sem classe são configuradas completamente no arquivo descritor XML da entidade. As entidades baseadas em classe definem seus atributos utilizando campos, propriedades e anotações Java. Portanto, as entidades sem classe precisam definir a estrutura de chave e atributo no descritor XML da entidade com as tags <basic> e <id>.

Metadados
da entidade sem classe
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://ibm.com/ws/projector/config/emd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://ibm.com/ws/projector/config/emd ./emd.xsd">

<entity class-name="@Employee" name="Employee">
    <attributes>
        <id name="serialNumber" type="long"/>
        <basic name="firstName" type="java.lang.String"/>
        <basic name="picture" type="[B"/>
        <version name="ver" type="int"/>
        <many-to-one name="department" 
            target-entity="@Department"
            fetch="EAGER"">
                <cascade><cascade-persist/></cascade>
        </many-to-one>
    </attributes>
</entity>

<entity class-name="@Department" name="Department" >
    <attributes>
        <id name="number" type="int"/>
        <basic name="name" type="java.lang.String"/>
        <version name="ver" type="int"/>
        <one-to-many name="employees" 
            target-entity="@Employee" 
            fetch="LAZY" 
            mapped-by="department">
            <cascade><cascade-all/></cascade>
        </one-to-many>
    </attributes>
</entity>

Observe que cada entidade acima tem um elemento <id>. Uma entidade sem classe deve ter um ou mais de um elemento <id> definido, ou uma associação com valor único que represente a chave para a entidade. Os campos da entidade são representados pelos elementos <basic>. Os elementos <id>, <version> e <basic> requerem um nome e um tipo nas entidades sem classe. Consulte a seguinte seção de tipos de atributo suportados para obter detalhes sobre os tipos suportados.

Requisitos de Classe da Entidade

As entidades baseadas em classe são identificadas por meio da associação de vários metadados com uma classe Java. Os metadados podem ser especificados utilizando anotações do Java Platform, Standard Edition 5, um arquivo descritor de metadados de entidade ou uma combinação de anotações e o arquivo descritor. As classes de entidade precisam atender os seguintes critérios: As entidades todas possuem um nome e tipo exclusivos. O nome, se utilizando anotações, é o nome simples (curto) da classe por padrão, mas pode ser substituído utilizando o atributo name da anotação @Entity.

Atributos Persistentes

O estado persistente de uma entidade é acessado por cliente e o entity manager utilizando qualquer um dos campos (variáveis de instância) ou acessores de propriedade do estilo Enterprise JavaBeans. Cada entidade deve definir um acesso baseado em campo ou em propriedade. Entidades anotadas são field-access se os campos de classe são anotados e property-access se o método getter da propriedade é anotada. Uma mistura de acesso por campo e acesso por propriedade não é permitida. Se o tipo não puder ser automaticamente determinado, o atributo accessType na anotação @Entity ou XML equivalente pode ser utilizado para identificar o tipo de acesso.

Campos persistentes
As variáveis de instância da entidade field-access são acessadas diretamente a partir do entity manager e dos clientes. Os campos que são marcados com o modificador transiente ou a anotação transiente são ignorados. Campos persistentes devem ter modificadores final ou static.
Propriedades persistentes
As entidades de acesso de propriedade devem seguir as convenções de assinatura do JavaBeans para as propriedades de leitura e gravação. Os métodos que não seguirem as convenções do JavaBeans ou possuírem a anotação Transient no método getter serão ignorados. Para uma propriedade do tipo T, deve haver um método getter getProperty que retorne um valor do tipo T e um método setter void setProperty(T). Para tipos booleanos, o método getter pode ser expresso como isProperty, retornando true ou false. As propriedades persistentes não podem ter um modificador estático.
Tipos de atributos suportados
O seguinte campo persistente e tipos de propriedades são suportados:
  • Os tipos primitivos Java incluindo wrappers
  • java.lang.String
  • java.math.BigInteger
  • java.math.BigDecimal
  • java.util.Date
  • java.util.Calendar
  • java.sql.Date
  • java.sql.Time
  • java.sql.Timestamp
  • byte[]
  • java.lang.Byte[]
  • char[]
  • java.lang.Character[]
  • enum
Tipos de atributos serializáveis são suportados mas possuem limitações de desempenho, consulta e detecção de alterações. Dados persistentes que não podem ser colocados em proxy, como matrizes e objetos serializáveis de usuário, devem ser reatribuídos para a entidade se alterados.

Os atributos serializáveis são representados no arquivo XML do descritor de entidade utilizando o nome de classe do objeto. Se o objeto for uma matriz, o tipo de dado será representado utilizando a forma interna Java. Por exemplo, se um tipo de dado de atributo for java.lang.Byte[][], a representação de cadeia será [[Ljava.lang.Byte;

Os tipos serializáveis de usuários devem obedecer às seguintes boas práticas:

  • Implemente métodos de serialização de alto desempenho. Implemente a interface java.lang.Cloneable e um método clone público.
  • Implemente a interface java.io.Externalizable.
  • Implemente iguais e hashCode

Associações de Entidade

Associações de entidades bidirecionais e unidirecionais, ou relacionamentos entre entidades podem ser definidos como um-para-um, muitos-para-um, um-para-muitos e muitos-para-muitos. O gerenciador de entidade resolve automaticamente os relacionamentos da entidade com as referências de chave apropriadas ao armazenar entidades.

A grade do eXtreme Scale é um cache de dados e não força integridade referencial como um banco de dados. Embora os relacionamentos permitam persistência em cascata e removam operações para entidades-filhas, eles não detectam ou impões links quebrados em objetos. Quando remover um objeto-filho, a referência a esse objeto deve ser removida do pai.

Se você definir uma associação bidirecional entre duas entidades, será preciso definir o proprietário do relacionamento. Em uma associação para-muitos, o lado muitos do relacionamento é sempre o lado do proprietário. Se a propriedade não puder ser determinada automaticamente, então, o atributo mappedBy da anotação ou equivalente XML, deve ser especificado. O atributo mappedBy identifica o campo na entidade de destino que é a proprietária do relacionamento. Este atributo também ajuda a identificar os campos relacionados quando há vários atributos do mesmo tipo e cardinalidade.

Associações com valor único

Associações um-para-um e muitos-para-um são indicadas utilizando anotações @OneToOne e @ManyToOne ou atributos XML equivalentes. O tipo de entidade de destino é determinado pelo tipo de atributo. O exemplo a seguir define uma associação unidirecional entre Person e Address.s A entidade Cliente tem uma referência a uma entidade Endereço. Nesse caso, a associação poderia também ser de muitos-para-um, já que não há relacionamento inverso.
@Entity
public class Customer {
  @Id id;
  @OneToOne Address homeAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
}
Para especificar um relacionamento bidirecional entre as classes Customer e Address, inclua uma referência à classe Customer a partir da classe Address e inclua a anotação apropriada para marcar o lado inverso do relacionamento. Como essa associação é um-para-um, você precisa especificar um proprietário do relacionamento utilizando o atributo mappedBy na anotação @OneToOne.
@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToOne(mappedBy="homeAddress") Customer customer;
}

Associações com valor de coleta

Associações um-para-muitos e muitos-para-muitos são denotadas utilizando as anotações @OneToMany e @ManyToMany ou atributos XML XML equivalente. Todos os relacionamentos muito são representados utilizando os tipos: java.util.Collection, java.util.List ou java.util.Set. O tipo de entidade de destino é determinado pelo tipo genérico de Collection, List ou Set ou explicitamente utilizando atributo targetEntity na anotação @OneToMany ou @ManyToMany (ou XML equivalente).
No exemplo anterior, não é prático ter um objeto address por cliente porque muitos clientes podem compartilhar um endereço ou podem ter vários endereços. Esta situação é melhor resolvida utilizando uma associação many:
@Entity
public class Customer {
  @Id id;
  @ManyToOne Address homeAddress;
  @ManyToOne Address workAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToMany(mappedBy="homeAddress") Collection<Customer> homeCustomers;


  @OneToMany(mappedBy="workAddress", targetEntity=Customer.class) 
		Collection workCustomers;
}
Neste exemplo, existem dois diferentes relacionamentos entre as mesmas entidades: um relacionamento de endereços Home e Work. Uma Collection não genérica é utilizada para o atributo workCustomers para demonstrar como utilizar o atributo targetEntity quando genéricos não estiverem disponíveis.

Associações sem classe

As associações de entidade sem classe são definidas no arquivo XML do descritor de metadados da entidade de forma semelhante ao modo como as associações baseadas em classe são definidas. A única diferença é que em vez de a entidade de destino apontar para uma classe real, ela aponta para o identificador da entidade sem classe utilizado para o nome de classe da entidade.

Este é um exemplo:

<many-to-one name="department" target-entity="@Department"
fetch="EAGER">
      <cascade><cascade-all/></cascade>
</many-to-one>
<one-to-many name="employees" target-entity="@Employee"
fetch="LAZY">
      <cascade><cascade-all/></cascade>
</one-to-many>

Chaves Primárias

Todas as entidades devem ter uma chave primária, que pode ser uma chave simples (atributo único) ou composta (atributos múltiplos). Os atributos-chave são denotados utilizando a anotação Id ou definidos no arquivo descritor XML da entidade. Atributos-chave possuem os seguintes requisitos: As chaves primárias compostas podem definir opcionalmente uma classe de chave primária. Uma entidade é associada a uma classe de chave primária com o uso da anotação @IdClass ou arquivo descritor XML da entidade. Uma anotação @IdClass é útil junto com o método EntityManager.find.
As classes de chave primária possuem os seguintes requisitos:
  • Ela deve ser pública com um construtor no-argument.
  • O tipo de acesso da classe de chave primária é determinado pela entidade que declara a classe da chave primária.
  • Se acesso por propriedade, as propriedades da classe de chave primária precisam ser públicas ou protegidas.
  • Os campos-chave primários ou propriedades devem corresponder aos nomes do atributo-chave e tipos definidos na entidade de referência.
  • As classes de chave primária devem implementar os métodos equals e hashCode.
Este é um exemplo:
@Entity
@IdClass(CustomerKey.class)
public class Customer {
    @Id @ManyToOne Zone zone;
    @Id int custId;
    String name;
    ...
}

@Entity
public class Zone{
    @Id String zoneCode;
    String name;
}

public class CustomerKey {
    Zone zone;
    int custId;

    public int hashCode() {...}
    public boolean equals(Object o) {...}
}

Chaves primárias sem classe

Entidades sem classe devem ter pelo menos um elemento <id> ou uma associação no arquivo XML com o atributo id=true. Um exemplo de ambos seria semelhante ao seguinte:

<id name="serialNumber" type="int"/>
<many-to-one name="department" target-entity="@Department"
id="true">
<cascade><cascade-all/></cascade>
</many-to-one>
Lembre-se:
A tag XML <id-class> não é suportada para entidades sem classe.

Proxies de Entidade e Intercepção de Campo

As classes de entidade e os tipos de atributo suportados mutáveis são estendidos pelas classes de proxy para entidades property-access e bytecode-enhanced para entidades field-access Java Development Kit (JDK) 5. Todos os acessos à entidade, mesmo por meio de métodos de negócios internos e dos métodos equals, devem utilizar o campo apropriado ou os métodos de acesso da propriedade.

Os proxies e interceptores de campo são utilizados para permitir que o entity manager controle o estado da entidade, determine se a entidade foi alterada e aprimore o desempenho. Os interceptores de campo estão disponíveis apenas em plataformas Java SE 5 quando o agente de instrumentação da entidade é configurado.

Atenção: Ao utilizar entidades property-access, o método equals deve utilizar o operador instanceof para comparar a instância atual com o objeto input. Toda a introspecção do objeto de destino deve ser por meio das propriedades do objeto, e não dos campos em si, pois a instância do objeto será o proxy.