Persistência usando Hibernate
Introdução
- Hibernate é um Framewok para mapeamento Objeto/Relacional em Java
- Possibilita desenvolver classes persistentes usando Java convencional:
- Associação
- Composição
- Herança
- Polimorfismo
- e coleções Java
- Implementa mecanismos de mapeamento:
- Classes Java <-> Tabelas em SGBDs relacionais
- Tipos Java <->Tipos SQL
- Implementa mecanimos convenientes para consulta e recuperação de dados
- Hibernate objetiva reduzir em cerca de 95% do tempo
de desenvolvimento de tarefas relacionadas à persistência!
Visão Geral
- A figura abaixo é uma descrição de alto nível da arquitetura do Hibernate

- O Hibernate persiste objetos java comuns (POJO)
- Usa reflexão para acessar as propriedades persistentes de um objeto
- As classes persistentes são definidades (descritas) em documentos de mapeamento
- Arquivos XML são usados para descrever os campos, associações e subclasses
persistentes
- Os Mapeamentos são "compilados" na inicialização da aplicação
- Podem ser usados também para operações de suporte como:
- Geração de esquemas do banco de dados
- Geração de código-fonte Java
Código típico no uso do Hibernate:
...
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Cliente cliente = new Cliente();
cliente.setNome("Maria");
cliente.setEndereco("Endereço de Maria");
cliente.setEmail("maria@xpto.com");
session.save(cliente);
tx.commit();
session.close();
...
- O código destacado está relacionado à operação do Hibernate
- O primeiro passo da aplicação é obter uma Session
- Esta é a principal interface entre a aplicação e o Hibernate
- A SessionFactory permite a aplicação criar sessões a
partir de arquivos de configuração hibernate.cfg.xml
- Após definir questões transacionais (opcional) a aplicação pode usar objetos
persistentes e a sessão hibernate para salvar dados no SGBD.
Iniciando uso do Hibernate
- O Hibernate funciona bem com o modelo POJO
- O uso de tipos de propriedades não é restrito
- É permitido qualquer tipo Java (incluindo coleções)
- É possível mapear valores, coleções de valores e associações com outros objetos
- A classe persistente não precisa implementar/herdar qualquer classe especial do framework
Exemplo:
import java.io.Serializable;
import java.util.Date;
public class Cliente implements Serializable {
private Long id;
private String nome;
private String endereco;
private String email;
private Date nascimento;
public Cliente(){
}
public void setId(Long id){
this.id = id;
}
public Long getId(){
return id;
}
public void setNome(String nome){
this.nome = nome;
}
public String getNome(){
return nome;
}
...
public void setNascimento(Date nascimento){
this.nascimento = nascimento;
}
public Date getNascimento(){
return nascimento;
}
}
- A classe Cliente precisa de um mapeamento para se tornar persistente
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "
-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="Cliente" table="CLIENTE">
<id column="CLIENTE_ID" name="id" type="java.lang.Long">
<generator class="sequence"/>
</id>
<property
column="NOME"
length="50"
name="nome"
not-null="true"
type="java.lang.String"
/>
...
<property
column="DATRA_NASCIMENTO"
name="nascimento"
type="java.util.Date"
/>
</class>
</hibernate-mapping>
- Os documentos de mapeamento podem ser gerados automaticamente a partir da base de dados
ou das classes java
- O elemento <class> mapeia uma classe a uma tabela
- O elemento <id> um atributo da classe à
chave-primária da tabela
- O elemento <property> mapeia os demais atributos do
objeto às colunas da tabela
- Por fim, é necesário criar o arquivo de configuração do hibernate:
<!DOCTYPE hibernate-configuration PUBLIC"
-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLMyISAMDialect</property>
<property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/exemplo</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">r00Tp@$wd</property>
<mapping-resource="Cliente.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- O arquivo de configuração provê informações ao framework para conectar-se
ao banco de dados
- O elemento <mapping-resource> aponta para o arquivo
de mapeamento
Mais sobre mapeamento:
Mapeamento de Associações
- Para mapear relações entre objetos persistentes usa-se os elementos <many-to-one> e <one-to-one>
- Mapeamento muitos-para-um com Cliente -> Dependentes
<many-to-one name="cliente"
column="CLIENTE_ID"
class="Cliente"
not-null="true"/>
- Mapeamento um-para-um com Cliente -> Usuário
<one-to-one name="usuario" class="Usuario"/>
Mapeamento de Coleções
- Hibernate persiste as seguintes coleções
- java.util.Map
- java.util.Set
- java.util.SortedMap
- java.util.SortedSet
- java.util.List
- e qualquer Array de objetos (ou valores) persistentes
- Propriedades do tipo java.util.Collection e java.util.List podem ser persistidos como
"bag"
- Coleções são mapeadas usando os elementos <set>, <list>, <map>, <bag>, <array> e <primitive-array>
- Uma tabela para a coleção é requirida quando ela contem valores ou referencias para
outras entidades mapeadas em muitos-para-muitos
- Segue um exemplo de mapeamento para uma coleção de String
<set name="enderecos" table="ENDERECOS">
<key column="CLIENTE_ID"/>
<element column="endereco" type="string"/>
</set>
- O elemento <key> indica a chave estrangeira na tabela
ENDERECOS
- Para relacionamentos um-para-muitos as tabelas das classes são ligadas diretamente (sem
uso de tableas intermediárias)
<set name="dependentes" inverse="true">
<key column="CLIENTE_ID"/>
<one-to-many class="Dependente" />
</set>
- Este é o mapeamento da coleção de dependentes de um Cliente
- o atributo inverse indica que Dependente também referencia
Cliente
<many-to-one name="cliente"
column="CLIENTE_ID"
class="Cliente"
not-null="true"
/>
- Em dependente é declarada a existencia da relação.
- Outras questões importantes:
- Lazy Initialization
- Relacionamentos ternários
Mapeamento de Herança
- O Hibernate suporta três estratégias de mapeamento de herança
- Uma tabela por hierarquia de classes
- Uma tabela por subclasse
- Uma tabela por classe concreta (polimorfismo implicito)
- Não é possível misturar as estratégias para um mesmo mapeamento de classe
- Suponha que tenhamos a classe Pessoa e as subclasses Cliente, Fornecedor e Funcionario
<class name="Pessoa" table="PESSOA">
<id name="id" type="long" column="PESSOA_ID">
<generator class="native"/>
</id>
<discriminator column="TIPO_PESSOA" type="string"/>
<property name="nome" column="NOME"/>
...
<subclass name="Cliente" discriminator-value="CLT">
...
</subclass>
<subclass name="Funcionario" discriminator-value="FCN">
...
</subclass>
<subclass name="Fornecedor" discriminator-value="FRC">
...
</subclass>
</class>
- No código acima temos a estratégia de uma tabela por hierarquia
- Apenas uma tabela é necessária, com todos os atributos da classe e suas subclasses
- A coluna <discriminator> indicará qual classe uma tupla representa
- A grande limitação é que colunas das subclasses não podem ser declaradas NOT-NULL
<class name="Pessoa" table="PESSOA">
<id name="id" type="long" column="PESSOA_ID">
<generator class="native"/>
</id>
<property name="nome" column="NOME"/>
...
<joined-subclass name="Cliente" table="CLIENTE">
<key column="PESSOA_ID"/>
...
</joined-subclass>
<joined-subclass name="Funcionario" table="FUCNIONARIO">
<key column="PESSOA_ID"/>
...
</joined-subclass>
<joined-subclass name="Fornecedor" table="FORNECEDOR">
<key column="PESSOA_ID"/>
...
</joined-subclass>
</class>
- No código acima temos a estratégia de uma tabela por subclasse
- Quatro tabelas serão usadas, uma para a superclasse e três para as subclasses
- As tabelas das subclasses estão ligadas à da superclasses via chave-estrangeira
- Melhor estratégia do ponto de vista relacional
<class name="Cliente" table="CLIENTE">
<id name="id" type="long" column="CLIENTE_ID">
<generator class="native"/>
</id>
<property name="nome" column="NOME"/>
...
</class>
<class name="Funcionario" table="FUNCIONARIO">
<id name="id" type="long" column="FUNCIONARIO_ID">
<generator class="native"/>
</id>
<property name="nome" column="NOME"/>
...
</class>
<class name="Fornecedor" table="FORNECEDOR">
<id name="id" type="long" column="FORNECEDOR_ID">
<generator class="native"/>
</id>
<property name="nome" column="NOME"/>
...
</class>
- No código acima temos a estratégia de uma tabela por classe concreta
- Note que a superclasse Pessoa não foi explicitamente mencionada
- Usa três tabelas, cada uma replicando os atributos de pessoa
- Para indicar o polimorfismo usa-se o elemento <any>
<any name="pessoa"
meta-type="class"
id-type="long">
<column name="PESSOA_CLASS"/>
<column name="PESSOA_ID"/>
</any>
Consultas
- As consultas usando Hibernate podem ser feitas de três formas
- Usando HQL (Hibernate Query Language)
- Usando Criteria Queries
- Usando SQL Nativo
Consultas HQL
- O HQL é uma linguagem SQL like, porém, "orientada a objetos"
- Possibilita descrever consultas polimorficas
- Possibilita consultas sobre coleções
from Pessoa pessoa where upper(pessoa.nome) like 'MARIA%'
- A consulta acima retorna todos os objetos da classe pessoa e de suas subclasses que
tenham o nome começado por MARIA
- Possibilita descrever consultas polimorficas
- Possibilita consultas sobre coleções
Query q = session.createQuery("from Pessoa pessoa where upper(pessoa.nome) like :NOME");
q.setProperties(nome);
List pessoas = q.list();
Criteria Queries
- É uma API (bastante intuitiva) extendível para executar consultas
- Para executá-la, basta criar um objeto Criteria e definir
nele os critérios da consulta
Criteria consulta = sess.createCriteria(Pessoa.class);
consulta.add( Expression.like("nome", "Maria%") );
consulta.add( Expression.between("idade", 18, 40) );
List resultado = consulta.list();
- A consulta acima vai retornar todas as Marias com idade entre 18 e 40 anos
Considerações
- O Hibernate é um framework consolidado para fazer pessistência
- Separa bem as coisas (o mundo OR do mundo OO)
- O overhead é extremamente satisfatório
- É mantido por uma comunidade muito ativa
- O maior esforço para usá-lo está na construção e manutenção dos mapeamentos
- Existem muitas ferramentas de apoio ao desenvolvimento usando Hibernate
- XDoclets
- Plugins para o Eclipse
- Uso adequado destas ferramentas deixa apenas o trabalho estritamente necessário para o
desenvolvedor
Recursos
daca programa