Mapeamento de atributos
- As entidades são convertidas para tabelas no banco..
- Os atributos simples desta entidade são convertidos às colunas da respectiva tabela.
Nesta seção vamos estudar como configura e utilizar as anotações nos atributos simples.
-Todas as anotações estão presentes no pacote
javax.persistence.*
@Column
Utilizamos esta anotação para configurar a coluna da tabela, quando não especificada as configurações default são aplicadas.
A anotação @Column permite algumas configurações em suas propriedades:
name: Define o nome da coluna, o valordefaulté o nome da propriedade ou atributo;unique: Informa se a coluna é chave única;columnDefinition: Defini o código SQL utilizado para gerar a coluna, o valordefaultcria a coluna com o tipo inferido.length: Tamanho da coluna, o valordefaulté 255, usado apenas em atributos com tipoString;precision: Informa a precisão da coluna decimal, usado apenas em atributos com tipo numérico decimal;scale: Define o número de casas decimais, usado apenas em atributos com tipo numérico decimal;table: Nome da tabela que contém está coluna;nullable: Informa se o valor pode sernull;updatable: Informa se a coluna deve ser incluída no SQL de update, o valordefaultétrue;insertable: Informa se a coluna deve ser incluída no SQL de insert, o valordefaultétrue;
Na documentação encontramos alguns exemplos de uso desta anotação:
@Column(name="DESC", nullable=false, length=512)
public String getDescription() {
return description;
}
@Column(name="DESC",columnDefinition="CLOB NOT NULL", table="EMP_DETAIL")
@Lob public String getDescription() {
return description;
}
@Column(name="ORDER_COST", updatable=false, precision=12, scale=2)
public BigDecimal getCost() {
return cost;
}
@Enumerated
Utilizamos esta anotação em atributos do tipo enum.
public enum Sexo {
MASCULINO,
FEMININO
}
Nota
Essa anotação permite dois valores ao parâmetro
EnumType:
@Enumerated(EnumType.STRING): Armazena a propriedade como um texto. Neste exemplo, seria persistido o texto "MASCULINO" ou "FEMININO", respectivamente para identificar oSexo.MASCULINOeSexo.FEMININO.@Enumerated(EnumType.ORDINAL): Armazena a propriedade como um inteiro. Neste exemplo, seriam persistido 0 ou 1, respectivamente para identificar oSexo.MASCULINOeSexo.FEMININO.
Mapeamento na entidade:
@Enumerated(EnumType.STRING)
private Sexo sexo;
@Transient
Há situações onde não temos interesse em persiste uma propriedade da entidade, pois podemos calcular o seu valor a partir de outro atributo, por exemplo calcular a idade sabendo de sua data de nascimento.
@Entity
public class Pessoa {
@Transient
private int idade;
private LocalDate dataDeNascimento;
Neste exemplo, o atributo idade não será persistido no banco de dados.
@Lob
Esta anotação é utilizada em atributos que representam uma grande quantidade de bytes para armazenamento, por exemplo imagens ou texto longos.
Em geral utilizada com @Basic, para sugerir que o atributo seja carregado sob demanda.
@Entity
public class Postagem {
@Lob
@Basic(fetch = FetchType.LAZY)
private String textoLongo;
Idealmente, devemos utilizar esta anotação também em conjunto com as anotações de relacionamento (@OneToMany e @ManyToMany).
@Basic
Esta anotação possui o parâmetro fetch(), que especifica se a propriedade é carregada sob demanda @Basic(fetch = FetchType.LAZY) ou imediatamente @Basic(fetch = FetchType.EAGER).
@Basic(fetch=LAZY)
protected String getName() { return name; }
Entretanto, o comportamento dessa propriedade não é garantido quando configurado com fetch = FetchType.LAZY, pois o provedor JPA pode, por questões de performance, carregar o valor do atributo.
@Entity
public class Pessoa {
@Lob
@Basic(fetch = FetchType.LAZY)
private byte[] imagem;
}
O outro parâmetro é o optional(), que define se o atributo pode, ou não, ser nulo.
@Temporal
Quanto temos propriedades que manipulam valores relacionados a tempo, usamos esta anotação. Podemos utilizá-la em propriedades to tipo java.util.Date ou java.util.Calendar.
@Entity
public class Pessoa {
@Temporal(TemporalType.DATE)
private Date dataDoNascimento;
}
Neste exemplo, o atributo dataDoNascimento é armazenado no banco como um tipo DATE do SQL.
Essa anotação permite três valores ao parâmetro TemporalType:
@Temporal(TemporalType.DATE): Recupera apenas a data (dia, mês e ano);@Temporal(TemporalType.TIME): Recupera apenas a hora (hora, minuto e segundo);@Temporal(TemporalType.TIMESTAMP): Recupera a data e hora (dia, mês, ano, hora, minuto e segundo).
@ElementCollection
Define uma coleção de instâncias de um tipo de dados ou classe embeddable. Deve ser especificado se a coleção está a sendo mapeada para uma tabela de coleção.
@Entity
public class Pessoa {
@ElementCollection
@CollectionTable(name = "Telefones")
private List<String> telefones;
}
Neste exemplo, a entidade Pessoa possui uma propriedade multivalorada. Será gerada uma tabela Telefones.
@Embeddable e @Embedded
@Entity
class Pessoa{
private String nome;
@Embedded
@AttributeOverride(name=valor,column=@Column(name=“cpf-pessoa”))
private CPF cpf;
}
@Embeddable
class CPF{
private String valor;
public String simples(){
return valor; //12345678910
}
public String formatado(){
return valor; //123.456.789-10
}
public boolean valido(){
return true; //false
}
}
@Converter
Quando temos que armazenar um atributo LocalDate em uma coluna Date ou uma LocalDateTime em uma coluna TIMESTAMP, precisamos definir um mapeamento para java.sql.Date ou java.sql.Timestamp.
Para criar um conversor de atributos para LocalDate e LocalDateTime, precisamos de uma classe que implemente AttributeConverter. Na classe ConvertLocalDate estamos convertendo um tipo LocalDate para java.sql.Date.
@Converter(autoApply = true)
public class ConvertLocalDate implements AttributeConverter<LocalDate, Date> {
@Override
public Date convertToDatabaseColumn(LocalDate attribute) {
if (attribute == null) {
return null;
}
return Date.valueOf(attribute);
}
@Override
public LocalDate convertToEntityAttribute(Date dbData) {
if (dbData == null) {
return null;
}
return dbData.toLocalDate();
}
}
Na entidade, usamos a anotação @Convert para identificar qual conversor será utilizado.
@Entity
public class Pessoa implements Serializable{
@Temporal(value = TemporalType.DATE)
@Convert(converter = ConvertLocalDate.class)
private LocalDate nascimento;
Chave composta
Temos duas estratégias @EmbeddedId e @IdClass.
Nota: A classe de chave primária, deve atender aos seguintes requisitos:
- Ela deve ser serializável;
- Ela deve ter um construtor sem argumento e público;
- Ela deve implementar os métodos
equals()ehashCode()A autogeração de valores não é suportada para chaves compostas.
@EmbeddedId
Embute a classe de chave primária diretamente na classe bean. Usada em conjunto com a @javax.persistence.Embeddable.
Possui 2 formas:
- Especificar os mapeamentos
@Column - Utilizar
@AttributeOverrides
@Embeddable
public class PessoaPK implements Serializable{
@Column(name=“PESSOA_NOME”) private String nome;
@Column(name=“PESSOA_IDADE”) private int idade;
public PessoaPK(){}
...
public boolean equals(Object obj){...}
public int hashCode(){}
}
Na entidade
@Entity
public class Pessoa implements Serializable{
}
private PessoaPK pk;
@EmbeddedId
public PessoaPK getPk(){return pk;}
A segunda forma fazendo uso da anotação @AttributeOverrides. Caso você não queira utilizar o @Column na classe de chave primária. Não precisa do @Column na classe anotada com @Embeddable (PessoaPK).
@Entity
public class Pessoa implements Serializable{
private PessoaPK pk;
}
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name=“nome”,column=@Column(name=“PESSOA_NOME”)),
@AttributeOverride(name=“idade”,column=@Column(name=“PESSOA_IDADE”)) })
public PessoaPK getPk(){return pk;}
@IdClass
public class PessoaPK implements Serializable{ private String nome;
private int idade;
public PessoaPK(){}
...
public boolean equals(Object obj){...} public int hashCode(){}
}
Na entidade
@Entity
@IdClass(PessoaPK.class)
public class Pessoa implements Serializable{
private String nome;
private int idade;
@Id
public String getNome(){return nome;}
@Id
public int getIdade(){return idade;}
Nota:
Em ambos os casos de uso de chave composta para realizar uma consulta devemos passar uma referência da classe que representa a chave composta.
PessoaPK pk = new PessoaPK("Outro Aluno", 26); Pessoa p = entityManager.find(Pessoa.class, pk);