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 valordefault
cria 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.MASCULINO
eSexo.FEMININO
.@Enumerated(EnumType.ORDINAL)
: Armazena a propriedade como um inteiro. Neste exemplo, seriam persistido 0 ou 1, respectivamente para identificar oSexo.MASCULINO
eSexo.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);