Neste artigo veremos como funcionam e quais os tipos que o JPA entende como “Persistentes”, ou seja, quais os tipos de dados que podem ser salvos no banco de dados através do JPA.
Entity Class
No Java Persistence API (JPA) uma Entidade ou Entity é uma classe definida pelo próprio usuário, onde suas instancias deverão ser salvas no banco de dados. A forma mas fácil e rápida de dizer ao JPA que sua classe é uma Entidade (Entity) é marcando a mesma com a anotação @Entity. Veja na Listagem 1.
Listagem 1. Criando uma classe @Entity
import javax.persistence.Entity;
@Entity
public class MyEntity {
}
Com a simples anotação acima o JPA já está apto a “encontrar” sua classe e realizar os devidos procedimentos nela, e que procedimentos seriam estes? Depende do que você está implementando na sua classe: Pode ser um relacionamento OneToOne, ManyToMany ou um mapeamento simples de uma coluna através do @Column ou qualquer outro recurso utilizado pelo JPA.
Existem ainda alguns requerimentos básicos para que o @Entity seja anotado na sua classe, veja quais são eles:
- A classe não poderá estar dentro de outra classe, ou seja, ser uma classe aninhada;
- A classe deve ter um construtor sem parâmetros (o construtor padrão) com a visibilidade public ou protected;
- A classe não pode ser final;
- Não pode ser estendida de uma classe de Sistema, como por exemplo a ArrayList.
Uma Entidade no JPA é como qualquer outra classe em Java (com exceção nas restrições acima). Você pode herdar a mesma de outra classe (usando o extends) ou mesmo implementar uma interface nela, ela poderá ter quantos construtores forem necessários, campos, métodos e etc.
Quando você cria uma entidade no JPA, ela poderá ser utilizada em diversas partes do seu código, como por exemplo em NamedQueries: Ao realizar uma operação DML (Data Manipulation Language) você referencia o nome da sua entidade usando o próprio nome da classe sem o pacote, ex: “SELECT m FROM MyEntity m”.
Mas podemos alterar esse nome através da propriedade 'name' inclusa na anotação @Entity, conforme a Listagem 2.
Listagem 2. Mudando nome utilizado pela Entitidade JPA
@Entity(name="MyName")
public class MyEntity {
}
Agora chamaremos o mesmo DML acima da seguinte forma: “SELECT m FROM MyName m”. Além disso, o atributo 'name' serve para evitar colisões entre nome de entidades, pois de acordo com a especificação do JPA, nomes de entidades devem ser únicas, ou seja, se você tiver duas classes distintas em pacotes distintos mas com nomes iguais, o atributo 'name' deve ser usado para especificar explicitamente o nome destas, afim de evitar uma colisão.
Mapped Supperclasses
Este tipo de classe serve apenas como “modelo” para outras subclasses, não é criada nenhuma tabela própria no banco de dados. Ele tem por objetivo mapear as informações nela contida nas classes que herdarem da mesma.
Vejamos o seguinte exemplo: Temos uma classe Employee com todos os dados padrões de um funcionário da empresa, mas o único objeto de termos esta classe é evitar a re-escrita diversas vezes das mesmas propriedades, pois teremos vários tipos de classes que podem ser herdadas de Employee, ex: Manager, Operator, Director e etc.
Para nossa lógica, só nos interessa guardas as classes filhas no banco de dados, pois como dissemos o Employee é apenas um 'modelo' e não deve refletir na base de dados. Veja a Listagem 3.
Listagem 3. Employee e FTEmployee
@MappedSuperclass
public class Employee {
@Id protected Integer empId;
@Version protected Integer version;
@ManyToOne @JoinColumn(name="ADDR")
protected Address address;
public Integer getEmpId() { ... }
public void setEmpId(Integer id) { ... }
public Address getAddress() { ... }
public void setAddress(Address addr) { ... }
}
// será criada a tabela FTEMPLOYEE
@Entity
public class FTEmployee extends Employee {
// herda campo empId de Employee e cria coluna FTEMPLOYEE.EMPID
// herda campo version de Employee e cria coluna FTEMPLOYEE.VERSION
// herda campo address de Employee e cria coluna FTEMPLOYEE.ADDR
//Campo nativo da classe FTEmployee, gera coluna FTEMPLOYEE.SALARY
protected Integer salary;
public FTEmployee() {}
public Integer getSalary() { ... }
public void setSalary(Integer salary) { ... }
}
Embeddable Class
Esse tipo de classe funciona como campos dentro de uma Entidade no JPA, isso significa entre outras coisas, que uma classe Embeddable só poderá ser salva no banco de dados, estando dentro de uma Entidade (Entity class).
Vejamos um exemplo na Listagem 4.
Listagem 4. Usando Embeddable
@Entity
@Table(name="tb_user")
public class User{
private String nome;
private Endereco endereco;
@Embedded
public Endereco getEndereco(){
}
}
@Embeddable
public class Endereco{
private String rua;
private Integer numero;
}
Acima temos duas classes: User e Endereco, onde User é uma Entidade e Endereco é uma classe embutida (Embeddable).
Com esquema acima é gerado apenas uma tabela chamada “tb_user” com os seguintes campos: nome, rua e numero. Perceba que a classe Endereco transformasse em campos para a tabela User.
E qual a utilidade de utilizar uma classe @Embeddable? Podemos ter diversas Entidades na nossa aplicação que utilize informações de endereços, por exemplo: User, Cliente, Funcionario e etc. Para padronizar que as informações de endereço sejam sempre as mesmas para todas nossas entidades, criamos a classe Endereco como @Embeddable e “injetamos” a mesma com a anotação @Embedded em todas as entidades que deverão possuir estes campos de endereço em comum.
A decisão de utilizar ou não uma classe @Embeddable depende diretamente de cada regra de negócio, pois alguns poderiam questionar: Porque não criar uma tabela tb_endereco e apenas fazer a relação com as classes necessárias? Bom, essa questão depende de cada caso e cabe a você analisar qual a melhor opção.
Tipo de Dados Simples
Fora as classes mostradas acima, que são tipos persistentes para o JPA, há também os tipos de dados que o mesmo suporta, são eles:
- Tipos Primitivos: boolean. byte, short, char, int, long, float e double.
- Wrappers: Boolean, Byte, Short, Character, Integer, Long, Float e Double.
- java.math.BigInteger, java.math.BigDecimal
- java.lang.String.
- java.util.Date, java.util.Calendar,java.sql.Date, java.sql.Time, java.sql.Timestamp.
Os tipos Date e Time tem algumas peculiaridades que veremos na próxima seção.
Tipos Date e Time
Quando utilizamos os tipos “java.util.Date” e “java.util.Calendar” deve-se explicitamente utilizar a anotação @Temporal afim de dizer se o tipo usado é apenas para Date ou Data e Hora em conjunto, isso acontece porque os tipos mencionados acima são genéricos e podem compreender tando Data como Data e Hora. Vejamos na Listagem 5 um exemplo.
Listagem 5. Usando @Temporal
@Entity
public class DatesAndTimes {
// Somente Data
java.sql.Date date1;
@Temporal(TemporalType.DATE) java.util.Date date2
@Temporal(TemporalType.DATE) java.util.Calendar date3;
// Somente hora
java.sql.Time time1;
@Temporal(TemporalType.TIME) java.util.Date time2;
@Temporal(TemporalType.TIME) java.util.Calendar time3;
// Data e hora
java.sql.Timestamp dateAndTime1;
@Temporal(TemporalType.TIMESTAMP) java.util.Date dateAndTime2;
@Temporal(TemporalType.TIMESTAMP) java.util.Calendar dateAndTime3;
java.util.Date dateAndTime4; // date and time but not JPA portable
java.util.Calendar dateAndTime5; // date and time but not JPA portable
}
Você pode optar por usar o pacote “java.sql.*” que possui as classes Date, Time e Timestamp, onde estas representam respectivamente Data, Hora e Data Hora juntos, sendo assim você não precisará usar a anotação @Temporal.
Campos com múltiplos valores
O JPA também suporta tipo de dados com múltiplos valores, são eles:
- Coleções: java.util: ArrayList, Vector, Stack, LinkedList, ArrayDeque, PriorityQueue, HashSet, LinkedHashSet, TreeSet.
- Map's: HashMap, Hashtable, WeakHashMap, IdentityHashMap, LinkedHashMap, TreeMap e Properties.
- Arrays, incluindo arrays multidimensionais.
Tipos Enumerados
O JPA também realiza o mapeamento necessário para tipos enumerados no do Java. Por padrão o JPA irá armazenar o número Ordinal do tipo enumerado selecionado, mas você poderá também armazenar valores alfanuméricos caso seja necessário. Veja a Listagem 6.
Listagem 6. Usando Enum com JPA
@Entity
public class Style {
Color color1; // default is EnumType.ORDINAL
@Enumerated(EnumType.ORDINAL) Color color2;
@Enumerated(EnumType.STRING) Color color3;
}
enum Color { RED, GREEN, BLUE };
Para a propriedade color2, será armazenado no banco de dados valores numéricos que correspondem ao Ordinal da classe Color, por outro lado na propriedade colo3 terá será armazenado valores alfanuméricos, que são “RED, GREEN ou BLUE”.
Vimos aqui vários tipos persistentes do JPA, desde Classes até as suas propriedades. Esperamos que com este artigo, seja possível entender e utilizar de forma correta os tipos que o JPA suporta, evitando futuras “dores de cabeça” ao tentar usar @Entity em uma classe com a palavra reservada 'final', por exemplo.