Neste artigo veremos conceitos e exemplos de como funcionam os campos de entidades persistentes do JPA (Entity, MappedSuperclass e Embeddable).

Na definição de uma entidade temos também que definir as propriedades que farão parte desta, como por exemplo: Uma entidade Pessoa terá provavelmente atributos como “nome”, “idade”, “endereco” e etc. Este artigo irá explicar que tipo de propriedades podemos utilizar “vinculadas” a nossa entidade JPA.

Antes de começarmos a explicar cada uma delas, iremos citá-las abaixo:

  • Propriedades Transientes
  • Propriedades Persistentes
  • Propriedades Inversas
  • Propriedades de Versionamento

Confira os Cursos online de Java da DevMedia

Os três primeiros itens citados (transientes, persistentes e inversas) podem ser utilizados estando em classes com a anotação “Entity” ou “Embeddable”, porém as duas últimas propriedades só podem ser utilizadas em classes com a anotação “Entity”.

Propriedades Transientes

Propriedades transientes são propriedades que não são salvas no banco de dados, ou seja, são campos que estão ali somente para armazenar valores temporários ou mais conhecidos como voláteis. Este conceito é muito similar ao conceito da palavra reservada “transient” em Java que inibe a serialização do campo.

Há algumas formas de dizer ao JPA que o seu campo é transiente e não deve ser persistido na base de dados, como mostrado na Listagem 1.

Listagem 1. Definindo campos transientes no JPA


  @Entity
  public class EntityWithTransientFields {
      
      //Campos definidos como estáticos (static) não são persistidos pelo JPA
      static int transient1;
   
      //Campos com a palavra reservada final também não são persistidos pelo JPA
      final int transient2 = 0;
   
      //A palavra reservada transient evita que o campo seja serializado e também que seja
      //persistido pelo JPA.
      transient int transient3; // not persistent because of transient
   
      //A anotação @Transient nos ajuda a dizer ao JPA explicitamente que não queremos
      //que determinado campo seja persistido
      @Transient int transient4;
  }

Acima já temos os comentários adequados, explicando a funciona de cada anotação ou palavra reservada para definir um campo transiente.

Propriedades Persistentes

Todos os campos que não são static, final ou não possuem a anotação @Transient são por padrão Persistentes, ou seja, serão persistidos pelo JPA. Pode ser um pouco óbvio mas fale ressaltar que o conceito de persistência não se aplica a métodos ou código da sua entidade, ou seja, não será salvo no banco de dados nenhum código ou método referente a sua classe mais sim os dados do estado persistente atual.

Quando uma propriedade for persistida no banco de dados ela deve conter o valor “null” ou um valor de um tipo persistente suportado pelo JPA. Não entraremos nos detalhes sobre os tipos persistentes que são suportados pelo JPA, apenas citaremos estes, e para não ficar estranha a tradução do termo em português, iremos utilizar em inglês até para facilitar no momento da sua “anotação” apropriada, são eles:

  • Entity class
  • Mapped superclass
  • Embeddable class
  • Tipos Primitivos, Wrappers, String, Date e tipos Matemáticos (Math)
  • Collections, Maps e Arrays
  • Enum (tipos enumerados), tipos Serializáveis (Serializable)

Todos as propriedades persistentes podem ser marcadas ainda com algumas anotações do JPA que vem para auxiliar no mapeamento do banco de dados relacionado para o mundo orientado a objetos, são elas:

  • @OneToOne, @ManyToOne – Usados para referenciar uma única Entidade (Entity)
  • @OneToMany, @ManyToMany – Usado para referencias collections (coleções) e maps de várias Entidades (Entity's).
  • @Basic, @Column – Usado para outros tipos persistentes

No JPA é facultativo o uso do @Basic ou @Column, pois ele já mapeia as propriedades persistentes automaticamente para você, por outro lado as anotações citadas acima que fazem relacionamento com outras entidades são obrigatórias. Vamos ver um exemplo de um classe (Entity) que possui três atributos persistentes na Listagem 2.

Listagem 2. Entity com atributos persistentes


  @Entity
  public class EntityWithFieldSettings {
      /* O atributo field1 será persistido no banco de dados,
      * além disso estamos usando o 'optional=false' que diz ao JPA
      * que este atributo 'field1' não poderá ser nulo, caso contrário uma
      * exceção será lançada
      */
      @Basic(optional=false) Integer field1;
   
      /*
      * O atributo 'field2' tem um relacionamento Um-para-Um com a classe
      * MyEntity, e o atributo 'cascade=CascadeType.ALL' identifica que ao realizar qualquer operação (inserir, remover, atualizar)
      * no MyEntity a classe EntityWithFieldSettings também será afetada
      */
      @OneToOne(cascade=CascadeType.ALL) MyEntity field2;
   
      /*
      * O atributo 'field3' mapeia uma lista de MyEntity dentro da classe EntityWithFieldSettings
      * e o atributo 'fetch=FetchType.EAGER' diz que essa lista deve ser sempre carregada do banco junto com a entidade
      */
      @OneToMany(fetch=FetchType.EAGER) List<MyEntity> field3;
  }

Acima demos uma explicação bem rápida sobre alguns conceitos das anotações utilizadas e suas propriedades mas não se foque nisto neste momento, pois o objetivo do artigo é mostrar conceitos de um nível de abstração maior e não entrar no nível de detalhamento sugerido na Listagem 2.

Ainda temos um outro tipo persistente que pode ser anotado dentro de nossa classe, o @Embedded, conforme a Listagem 3.

Listagem 3. Tipo persistente @Embedded


  @Entity
  public class Person {
      @Embedded Address address;
  }

O JPA irá garantir (com a anotação acima @Embedded) que o objeto Address só será salvo junto com o objeto Person, por isso ele é “embutido” dentro deste. Não faz sentido para nossa regra de negócio, salvar o Address em um contexto separado. Lembre-se que a anotação @Embedded é utilizada na propriedade address apenas, pois na declaração da classe Address deve-se utilizar @Embeddable.

Até o momento vimos os tipos “Transientes” e “Persistentes” que podem ser utilizados tanto em classes Embutidas como em classes “Entity”, ou seja, @Embeddable class e @Entity class. Ou seja, tudo que você viu até agora pode ser utilizado nesses dois tipos de classes. Agora veremos algumas outras propriedades que não podem ser utilizadas em classes @Embeddable, apenas em @Entity.

Propriedades Inversas

Propriedades inversas são propriedades que não são fisicamente salvas no banco de dados mas podem ser acessadas em tempo de execução através de queries automáticas.

A navegação por propriedades inversas é muito mais lenta que a navegação direta em campos persistentes, pois diferente dos campos persistentes que já estão salvos no banco as propriedades inversas realiza uma consulta para buscar o valor em tempo de execução. Isso significa que caso você tenha como prioridade velocidade na sua aplicação, então evite propriedades inversas.

Vamos ver o exemplo da Listagem 4, que explica como funciona esse tipo de propriedade com mais detalhes.

Listagem 4. Usando propriedades inversas


  @Entity
  public class Employee {
      String name;
      @ManyToOne Department department;
  }
   
  @Entity
  public class Department {
      @OneToMany(mappedBy="department") Set<Employee> employees;
  }

Acima temos duas entidades: Employee (Empregado) e Departament (Departamento). Um Departamento pode ter vários empregados, porém um Empregado só pode estar em Um Departamento.

Quando criarmos o relacionamento no JPA, normalmente iremos partir do Empregado para o Departamento, realizando um mapeamento @ManyToOne e isso é suficiente para que todo nosso relacionamento funcione. Mas caso precisarmos acessar todos os Empregados a partir de um Departamento temos duas opções:

  1. Criar uma propriedade inversa com o @OneToMany usando o mappedBy, assim quando acessarmos a propriedades “employees” dentro da classe Departament, será feita uma consulta em busca de todos esses empregados.
  2. Realizar a consulta externa sem a necessidade de um mapeamento com propriedade inversa.

Propriedade de Versionamento

Essa propriedade garante uma técnica chamada como “Lock Otimista” ou “Optimistic locking”, que em resumo, evita colisões de atualizações concorrentes na mesma entidade. Não entraremos em detalhes do funcionamento desta técnica, mas explicaremos o uso da anotação @Version na Listagem 5.

Listagem 5. Usando anotação @Version


  @Entity
  public class MyEntity implements Serializable {    
   
      @Id
      @GeneratedValue
      private Long id;
   
      private String name;
   
      @Version
      private Long version;
   
      //...
  }

Toda vez que uma alteração transacional é realizada em uma entidade sua versão é incrementada em 1. Por exemplo: Na classe da Listagem 5 quando uma atualização tenta ser feita um UPDATE. como é realizado na Listagem 6.

Listagem 6. Update com Version


  UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))
  

Se uma atualização for realizada antes da nossa atualização, a versão do objeto já foi alterada e quando tentarmos executar o código da Listagem 6 teremos uma exceção OptimisticLockException.

Assim garantimos a consistência dos dados quando precisamos trabalhar de forma concorrente.