Utilizando o Hibernate temos a opção de criar mapeamentos entre modelos de base de dados e modelos de objetos através de arquivos XML ou através de anotações no código fonte dos objetos POJO.
Utilizar anotações nos oferece diversas vantagens como um código mais intuitivo do que a utilização de arquivos baseados em XML, menos detalhes para se preocupar do que arquivos XML equivalentes, etc.
Hibernate usa e suporta anotações JPA 2. No restante do artigo estaremos estudando como mapear as tabelas, colunas e omitir determinados campos.
Quem quiser se aprofundar ainda mais nesse framework, sugiro dar uma olhada nesse Curso de Java da DevMedia que fala exclusivamente sobre o Hibernate
1. Mapeando Tabelas com @Table e @SecondaryTable
Utilizando a anotação @Table podemos especificar detalhes da tabela que serão utilizados para persistir as nossas entidades na base de dados. Caso essa anotação seja omitida, não teremos um erro como resultado, porém será utilizado o nome da classe como valor default. Dessa forma, apenas definimos a anotação se quisermos sobrescrever o nome da classe.
A anotação @Table possui quatro atributos que possibilitam ao desenvolver sobrescrever o nome da tabela, como dito anteriormente, sobrescrever seu catálogo, seu esquema e assegurar restrições de unicidade nas colunas da tabela. Para definirmos o nome da tabela é muito simples, o exemplo da Listagem 1 demonstra como fazer isso.
Listagem 1. Definindo o nome da tabela com a anotação Table.
import javax.persistence.*;
@Entity
@Table(name="TABELA_EXEMPLO")
public class Exemplo {
@Id
public Integer codigo;
public String nome;
}
As restrições de unicidade apenas são aplicadas se o esquema da base de dados é gerado através das classes anotadas, e irá complementar quaisquer restrições específicas de coluna.
Outra anotação que o Hibernate oferece é a @SecondaryTable que nos permite modelar uma entidade que é persistida entre várias diferentes tabelas de uma base de dados.
A anotação @SecondaryTable é fornecida junto com a anotação @Table e possui os mesmo atributos que @Table com a única diferença que esta possui o atributo join. Além disso, podemos usar também a anotação @SecondaryTables caso tenhamos zero ou mais anotações @SecondaryTable. O atributo join define uma coluna de junção para a tabela da base de dados primária aceitando um array de objetos do tipo javax.persistence.PrimaryKeyJoinColumn. Caso o atributo join seja omitido será assumido que as tabelas são unidas através de uma coluna de chave-primária com nomes idênticos.
Quando um atributo na entidade é fornecido pela tabela secundária devemos marcar a anotação @Column com um atributo "table" que identifique a tabela secundária apropriada. Na Listagem 2 temos um exemplo de um cliente que possui dados adicionais em uma tabela secundária mapeada.
Listagem 2. Acessando campos da tabela secundária
import javax.persistence.*;
@Entity
@Table(name="CLIENTE")
@SecondaryTable(name="CLIENTE_DETALHES")
public class Cliente {
@Id
public int id;
public String nome;
@Column(table="CLIENTE_DETALHES")
public String endereco;
}
Também podemos marcar como contendo valores únicos as colunas da tabela primária ou da tabela secundária adicionando uma ou mais anotações @UniqueConstraint para @Table ou @SecondaryTable dentro de um atributo uniqueConstraints. Na Listagem 3 temos um exemplo de como podemos proceder.
Listagem 3. Definindo valores únicos.
@Entity
@Table(
name="CLIENTE",
uniqueConstraints={@UniqueConstraint(columnNames="nome")}
)
@SecondaryTable(name="CLIENTE_DETALHES")
public class Cliente {
...
}
2. Persistindo Tipos Básicos com @Basic
Propriedades e variáveis de instância, por default, são persistentes. O Hibernate sempre irá armazenar esses valores para nós. Dessa forma, o mapeamento mais simples são os tipos básicos. Esses tipos incluem primitivos, primitivos wrappers, arrays de primitivos, arrays de primitivos wrappers, enumerações e qualquer tipo que implementa Serializable não sendo entidades mapeadas.
Portanto, todos esses tipos são mapeados implicitamente, não necessitando de anotações. Esses tipos básicos são mapeados para colunas simples e utilizam a busca EAGER que é usada para recuperá-las.
Eventualmente queremos sobrescrever esse comportamento padrão. Para isso devemos utilizar a anotação @Basic para a propriedade apropriada. A anotação @Basic requer dois atributos opcionais. O primeiro atributo é o optional e requer um valor boleano, o padrão é true. Se esse valor for setado para false tem-se que a coluna será do tipo NOT NULL quando criarmos um esquema. O segundo atributo é o fetch que tem como valor um membro de uma enumeração. Por default o valor é EAGER, mas também podemos setar para LAZY permitindo carregar o valor apenas quando acessarmos ele. O uso de LAZY é mais indicado quando um grande objeto serializável é mapeado como um tipo básico e o seu tempo de recuperação é significante.
De forma geral o atributo @Basic é frequentemente omitido. Mas nada impede que ele seja utilizado. Na Listagem 4 demonstramos um exemplo da utilização de @Basic.
Listagem 4. Utilizando o atributo @Basic.
@Entity
public class Mensagem implements Serializable{
@Id
@GeneratedValue Long id;
@Basic
@Column(nullable=false, length=100)
String tituloMensagem;
@Basic
@Column(nullable=false, length=5000)
String textoMensagem;
//gets e sets aqui
}
O atributo @Basic nesse caso poderia ter sido omitido, afinal de contas ele não sobrescreve nenhum atributo default.
3. Evitando a Persistência com @Transient
Existem algumas situações em que um atributo é utilizado apenas como sendo um auxiliar. Por exemplo, atributos para valores calculados são utilizados apenas em tempo de execução e devem ser descartados após finalizar o seu serviço temporário. A especificação do EJB 3 prove a anotação @Transient para esses campos que não precisam ser armazenados na base de dados. Essa anotação não tem qualquer atributo. Sua utilização é bem simples bastando apenas adicionar a anotação na variável de instância ou no método getter apropriado. O exemplo da Listagem 5 mostra um atributo que não é armazenado na base de dados.
Listagem 5. Utilizando @Transient para atributos não persistentes.
@Transient
public Date getValorSaldoTotal() {
return valorSaldoTotal;
}
4. Mapeando Campos e Propriedades com @Column
A anotação @Column é utilizada para especificar os detalhes da coluna que um campo ou propriedade será mapeado. Alguns detalhes são relacionados com o esquema e, portanto aplicados apenas quando um esquema for gerado. A anotação @Column é opcional, possuindo valores default já previamente configurados. Essa anotação é extremamente útil quando temos um esquema já preexistente e precisamos ajustar nosso modelo de objetos a esses esquemas preexistentes. Dessa forma, podemos sobrescrever os valores default. A anotação @Column é muito mais comum de ser utilizada do que a anotação @Basic.
Alguns atributos normalmente são sobrescritos, segue abaixo quais são esses atributos e para que eles são utilizados:
- name: utilizado para especificar explicitamente o nome da coluna. Por default, o nome utilizado será o mesmo que utilizado pela propriedade.
- length: utilizado para especificar o tamanho da coluna. O tamanho default da coluna é 255.
- nullable: utilizado para especificar a coluna ser marcada como NOT NULL quando o esquema é gerado. O valor default é que o campo pode ser NULL.
- unique: utilizado para especificar colunas a serem marcadas como possuindo valores únicos. O valor default é false. Alguns valores não são chave-primária, no entanto, precisam possuir valores únicos, como username, que causam problemas caso sejam duplicados.
Na Listagem 6 temos um exemplo de utilização dos principais atributos de @Column.
Listagem 6. Utilizando atributos da anotação @Column.
@Column(name="titulo_trabalho",length=200,nullable=false)
public String getTitulo() {
return titulo;
}
Ainda temos outros atributos, porém menos utilizados:
- table: utilizada quando a entidade proprietária foi mapeada entre uma ou mais tabelas secundárias.
- insertable: utilizada para configurar se o campo anotado será omitido em comandos de inserção. O valor padrão é true.
- updatable: utilizada para configurar se o campo anotado será omitido em comandos de atualização (update). O valor padrão é true.
- columnDefinition: utilizada na geração de esquemas. Pode ser configurada para um fragmento de DDL apropriado para ser usado quando gerarmos as colunas na base de dados.
- precision: utilizada para definirmos a precisão de colunas com número decimais. Esse atributo é utilizado na geração dos esquemas e será ignorada quando um valor não decimal é persistido.
- scale: utilizada para definirmos a escala de colunas com número decimais. Esse atributo é utilizado na geração dos esquemas e será ignorada quando um valor não decimal é persistido.
Com isso, neste artigo vimos como mapear tabelas e colunas utilizando o Hibernate. Também vimos como omitir certos campos e quais são os principais e mais utilizados atributos para cada uma das anotações vistas.
Até a próxima!
Bibliografia
[1]Hibernate - JBoss Community, disponível em www.hibernate.org/
[2]Documentação de Referência Hibernate, disponível em https://docs.jboss.org/hibernate/core/3.6/reference/pt-BR/html/index.htmll
[3] Jeff Linwood and Dave Minter. An introduction to persistence using Hibernate 3.5, Second Edition. Apress.
[4] Steve Perkins. Hibernate Search by Example: Explore the Hibernate Search system and use its extraordinary search features in your own applications. Packt Publishing.