Buscar

Introducao ao JPA e ao Hibernate

Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original

*
*
Tópicos em Engenharia de Software II – 2011/2
Prof. Vitor Alcântara Batista
*
*
Introdução
Arquitetura
Mapeamento de Entidades
Mapeamento de Associações
Persistindo objetos
Consultando objetos
*
*
Java Persistence API
É um padrão definido pelo JCP para trabalhar com persistência de dados.
O líder a especificação foi Gavin King, o criador do Hibernate.
Existem várias implementações disponíveis no mercado.
Hibernate, SAP Netweaver AS, TopLink, EclipseLink, Open,JPA, Kodo, JPOX, Amber, entre outras...
*
*
Hibernate é um framework de mapeamento Objeto/Relacional para Java e .NET(NHibernate)
Oferece serviços para consulta e recuperação de dados
Objetivo: liberar o desenvolvedor de grande parte da programação relacionada a persistência de dados
Suporte aos principais SGDBs
*
*
Projeto Hibernate
Mantido atualmente pelo JBoss Inc.
Projeto Open-source
Adotado por muitos projetos no mundo inteiro
Versão 3 foi base para a especificação de persistência da JPA
Hibernate implementa a JPA.
*
*
*
*
Principais componentes
Configuration
SessionFactory
Session
Query
Criteria
Configuração
*
*
org.hibernate.cfg.Configuration
Classe responsável por carregar toda a configuração do hibernate
Mapeamento de classes
Configurações de banco de dados
Pode ser criado a partir de arquivos de configuração
hibernate.cfg.xml	
hibernate.properties
Pode ter suas propriedades alteradas em tempo de execução
Fábrica de SessionFactory
*
*
org.hibernate.SessionFactory 
Fábrica responsável pela criação das sessões
Cache dos mapeamentos compilados
Possui um ciclo de vida longo, geralmente durante todo o período de atividade da aplicação
É threadsafe (imutável) para uma única instância de banco de dados.
*
*
org.hibernate.Session 
Representa uma conversação entre a aplicação e o banco de dados (BD)
Possui um tempo de vida curto, geralmente associado a uma única transação com o BD
Responsável por quase todos os métodos de persistência
Fábrica de transações
*
*
Estados de uma instância de objeto:
Transiente: A instância não está, e nunca esteve associada a um contexto de persistência. O objeto não possui identificador (chave primária).
Persistente: A instância está associada a um contexto de persistência. Possui identificador.
Detached: A instância já esteve associada com um contexto de persistência, porém o contexto foi fechado ou a instância foi serializada por outro contexto.
Removed: A instância está marcada para ser excluída, mas ainda não foi excluída.
*
*
*
*
org.hibernate.Query
Interface utilizada para pesquisas no BD
Disponível para pesquisas utilizando
SQL
HQL
Oferece métodos de paginação de resultados
Parametrização de restrições na cláusula WHERE
*
*
org.hibernate.Criteria
Interface utilizada para pesquisas no BD
Semelhante ao Query, mas utiliza uma estrutura de classes para representar a pesquisa
Ex:
List cats = session.createCriteria(Cat.class) 
	.add( Restrictions.like("name", "F%")
	.addOrder( Order.asc("name") )
	.addOrder( Order.desc("age") )
	.setMaxResults(50) 
	.list(); 
*
*
hibernate.cfg.xml
Contém informações sobre os arquivos de mapeamento
Pode conter as informações necessárias para a conexão do banco de dados
Deve estar no classpath
O nome do arquivo pode ser configurado
hibernate.properties
Pode conter as informações de configuração da conexão e banco de dados
Deve estar no classpath
*
*
Mapeamento pode ser feito em arquivos xml separados do código fonte
Pode ser feito com tags xDoclet no código fonte
Pode ser feito com Annotations do Java 1.5 e Hibernate 3
*
*
Entidades:
“Tipicamente” representam uma tabela no banco de dados.
Cada instância de uma entidade corresponde a uma linha na tabela.
Seguindo os padrões da JPA cada entidade DEVE atender os requisitos:
Ser anotada coma anotação @Entity.
Deve ter um construtor sem argumentos, público ou protegido.
Não pode ser declarada final. Nenhum método ou variável de instância pode ser declarada como final.
Variáveis de instância persistentes não devem ser declaradas públicas e só podem ser acessadas pelos métodos da classe.
*
*
Uma Entidade é mapeada com a anotação
@Entity
Pode ser definido um nome para a tabela do banco de dados através do atributo name da anotação.
Um objeto que não é uma entidade, mas que tem atributos persistentes ou informações de mapeamento, deve ser mapeado com a anotação @MappedSuperclass
Objetos mapeados com a anotação @MappedSuperclass não podem ser pesquisados no banco, ou seja, não existe uma tabela para aquele objeto. 
A anotação @MappedSuperclass é utilizada para hierarquias de objetos.
*
*
Entidades que extendem não-Entidades ao serem salvas, ignoram os dados da superclasse, a não ser que a superclasse utilize a anotação @MappedSuperclass
Ou seja, os dados da superclasse são transientes e nunca serão persistentes.
*
*
Como anotar a entidade
import javax.persistence.Entity;
@Entity 
public class Aluno 
{
}
*
*
*
*
Existem 3 estratégias de mapeamento de polimorfismo
Uma tabela para toda a hierarquia de classes
Uma tabela por classe concreta
Uma tabela por subclasse
Flexibilidade
Desempenho
*
*
Uma tabela para toda a hierarquia de classes
Toda a hierarquia de classes é representada por uma única tabela no banco de dados
Propriedades de uma subclasse específica não podem ser anotadas com a anotação @NotNull
Utiliza a anotação @DiscriminatorColumn para identificar a instância
*
*
Uma tabela para toda a hierarquia de classe
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name=“TYPE",
discriminatorType=DiscriminatorType.STRING)
public class Person { …}
@Entity
@DiscriminatorValue(“EMP")
public class Employee { … }
@Entity
@DiscriminatorValue(“STU")
public class Student { … }
*
*
Uma tabela para toda a hierarquia de classe:
@Inheritance e @DiscriminatorColumn só devem ser utilizados no topo da hierarquia.
Caso não seja informado um name para o DiscriminatorType o default é DTYPE
Caso não seja informado um valor para o DiscriminatorValue o default é o nome da entidade, conforme definido na anotação @Entity.
*
*
Uma tabela por classe concreta
Cada classe concreta será armazenada em uma tabela distinta
*
*
Uma tabela por classe concreta
A classe abstrata Person não é mapeada
As sub-classes são mapeadas como uma classe qualquer
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Person { … }
@Entity
public class Student extends Person { … }
@Entity
public class Employee extends Person { … }
*
*
Uma tabela por classe concreta
Cada classe será armazenada em uma tabela
Estratégia mais “orientada à objeto”
Necessita de mais joins entre tabelas para recuperar dados.
Não suporta muito bem associações polimórficas.
*
*
Uma tabela por classe
*
*
Uma tabela por classe
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Person { …}
@Entity
public class Employee { … }
@Entity
@PrimaryKeyJoinColumn(name=“Person_ID")
public class Student { … }
*
*
Uma tabela por classe
Cada classe será armazenada em uma tabela, inclusive a classe pai, sendo ela abstrata ou não.
Necessita de um único join para recuperar os dados.
Pode impactar no desempenho de uma aplicação devido ao número de joins que podem ser feitos em uma hierarquia completa.
*
*
A primeira estratégia é a mais simples mas restringe a utilização de not-null
Utilizado quando a principal diferença entre as diferentes sub-classes é seu comportamento, possuindo quase o mesmo conjunto de propriedades
A segunda resolve o problema do not-null e da diferença entre o conjunto de propriedades
Mas apresenta problema com associação polimórfica
A terceira
é a mais flexível
Permite associações com classes abstratas
Mas necessita de mais joins para recuperar dados
*
*
A anotação @Id define o identificador único da classe
Toda classe deve declarar um identificador obrigatoriamente
A anotação @GeneratedValue é utilizada para definir a forma com que o id será gerado.
@Id
@GeneratedValue (strategy=GenerationType.AUTO)
public Long getId(){}
*
*
toda entidade é herdeira de ObjetoPersistente, que contém o Identificador
@MappedSuperclass public abstract class ObjetoPersistente 
 implements Comparable<Object>
{
private Long id;
@Id @GeneratedValue public final Long getId()
@Entity public final class Usuário extends EntidadePersistente
*
*
Tipos de associações:
1 – 1
1 – N (composição)
1 – N
N - N
*
*
A entidade que possui a chave estrangeira é responsável pela associação (a associação é persistida quando o objeto é persistido).
 @OneToOne
 @JoinColumn
public Endereço getEndereco(){…}
 @OneToOne(mappedBy=“endereco”)
public Pessoa getPessoa(){…}
*
*
Uma tabela é criada com o nome de PessoaEndereco e em cada linha armazenará o id da Pessoa e o id do Endereço nas colunas pessoa_fk e endereco_fk,respectivamente.
 @OneToOne (cascade=CascadeType.ALL)
 @JoinTable(name=“PessoaEndereco",
joinColumns = @JoinColumn(name = “pessoa_fk"),
inverseJoinColumns = @JoinColumn(name = “endereco_fk") )
public Endereço getEndereco(){…}
 @OneToOne(mappedBy=“endereco”)
public Pessoa getPessoa(){…}
*
*
Cuidado com o nome das chaves, eles são os responsáveis por tornar a associação bidirecional
As opções de cascade devem ser feitas de acordo com o tipo de associação
Se existir uma composição entre Pessoa e Dependente, a opção de cascade do lado de Pessoa deveria ser “all-delete-orphan ”
Isso vincularia o ciclo de vida de Dependente a Pessoa.
@OneToMany(mappedBy=“responsavel“)
public Collection getDependetes(){…}
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn
@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
public Pessoa getResponsavel(){…}
*
*
Também é possível fazer o mapeamento ManyToOne e OneToMany utilizando uma tabela secundária.
O comportamento é o mesmo do mapeamento OneToOne com tabela secundária.
Este caso é recomendado quando o relacionamento é unidirecional em casos OneToMany.
@OneToMany
@JoinTable(name=“Pessoa_Dependente",
joinColumns = @JoinColumn(name = “pessoa_id"), inverseJoinColumns = @JoinColumn(name =“dependente_id") )
public Collection getDependetes(){…}
@Entity
public class Dependente
{
…
//Não existe o atributo pessoa
... 
}
*
*
Em associações 1 – N SEMPRE o lado N comandará a associação
A associação é persistida quando Dependente for persistido.
Esta regra é uma boa prática, pois é otimizada e em geral executará apenas uma ação via cascade ao invés de N.
@OneToMany(mappedBy=“responsavel")
public Collection getDependetes(){…}
@ManyToOne
@JoinColumn(name=“PESSOA_DEP")
public Pessoa getResponsavel(){…}
*
*
Em mapeamento many-to-many é importante utilizar o mappedBy para indicar qual lado manda na relação.
Cascade para deleção deve ser utilizado com cuidado, já que uma entidade com diversas associações pode ser apagada quando somente um das entidades associados for apagada.
@ManyToMany(mappedBy=“pessoas")
public Collection getFuncoes(){…}
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@JoinTable
public Pessoa getPessoas(){…}
*
*
Importância do cascade
O cascade define a propagação das operações de persistência
Importante para o ciclo de vida das entidades relacionadas
Cascade mal planejado pode destruir uma aplicação
Problemas de desempenho
Perda de dados por cascade - CascadeType.Remove
Deixar “lixo” no banco de dados
*
*
Persist: Propaga a operação de persistência para os objetos associados se o método persist() é chamado ou se a entity é gerenciada.
Merge: Propaga a operação de merge para os objetos associados se o método merge() é chamado ou se a entity é gerenciada.
Remove: Propaga a operação de deleção para os objetos associados se o método delete() é chamado.
Refresh: Propaga a operação de atualização para os objetos associados se o método refresh() é chamado.
All:Todos os anteriores.
*
*
Eager: Utiliza um outer join para recuperar os objetos associados quando o objeto base é carregado.
Lazy: Carrega os objetos associados somente quando eles forem utilizados pelo objeto base.
Relacionamentos OneToMany e ManyToMany têm por default o tipo de recuperação Lazy, enquanto os relacionamentos OneToOne e ManyToOne tem o tipo de recuperação Eager.
@ManyToMany (fetch=FetchType.EAGER)
*
*
Principais Interfaces do JPA
EntityManagerFactory
EntityManager
EntityTransaction
*
*
Fornece instâncias de EntityManager
Todas as instâncias fornecidas, conectam num mesmo banco e são geradas, por padrão, com as mesmas configurações.
A fábrica, usualmente, está associada à aplicação e tem o mesmo ciclo de vida dela
*
*
private static EntityManagerFactory emf;
emf = Persistence.createEntityManagerFactory("praxis");
<persistence-unit name="praxis">
<properties>
	<property name="hibernate.archive.autodetection“ value="class, hbm" />
	<property name="hibernate.show_sql" value="true" />
	<property name="hibernate.format_sql" value="true" />
	<property name="hibernate.connection.driver_class"
value="org.hsqldb.jdbcDriver" />
	<property name="hibernate.connection.url"
value="jdbc:hsqldb:hsql://localhost:9001/" />
	<property name="hibernate.connection.username" value="sa" />
	<property name="hibernate.dialect"
value="org.hibernate.dialect.HSQLDialect" />
	<property name="hibernate.hbm2ddl.auto" value="create-dropk" />
</properties>
</persistence-unit>
Arquivo persistence.xml
*
*
Semelhante à classe Session do Hibernate.
Utilizada para persistir, excluir, recuperar pelo ID e fazer consulta por objetos.
Principais métodos
persists(Object)
remove(Object)
find(Class<T>, Object)
getReference(Class<T>, Object)
createQuery(String)
getTransaction()
*
*
Persistindo um objeto
Recuperando um objeto pelo ID
EntityManager em = emf.createEntityManager();
Aluno a = new Aluno();
a.setNome("Vitor");
em.persist(a);
EntityManager em = emf.createEntityManager();
Aluno a = em.find(Aluno.class, new Long(1));
*
*
Recuperando todos os objetos de uma classe
Recuperando objetos de um tipo com filtro
List<Aluno> alunos = em.createQuery("from Aluno").getResultList();
List<Pessoa> alunos = em.createQuery(
 "select nome from Aluno as aluno where aluno.nome = ?1")
 .setParameter(1, "Vitor")
 .getResultList();
Query q = em.createQuery("select nome from Aluno as aluno where 	aluno.nome = :nome");
q.setParameter(“nome”, “Vitor”);
List<Pessoa> alunos = q.getResultList();
*
*
Só é necessário fazer joins quando deseja-se filtrar o resultado por um atributo de outra entidade.
Ao recuperar um objeto, é possível navegar pelas associações e o hibernate recupera as instâncias associadas.
*
*
HQL
Consulta executada pelo Hibernate
List<Pessoa> alunos = em.createQuery(
 "select aluno.nome from Aluno as aluno JOIN aluno.endereco as endereco 	where endereco.cidade = ?1“).setParameter(1, "BH“) 	.getResultList();
 select
 aluno.nome
 from
 Aluno
 inner join
 Pessoa on Pessoa.id=Aluno.id 
 inner join
 Endereco on Pessoa.id=Endereco.pessoa_id 
 where
 Endereco.cidade=?
*
*
Criar um novo projeto, configurar o Hibernate e mapear um modelo de Entidades.
*
*
Criar código Java (uma nova classe executável) para reproduzir um conjunto de objetos.
Executar consultas nesses objetos e mostrá-los no console

Teste o Premium para desbloquear

Aproveite todos os benefícios por 3 dias sem pagar! 😉
Já tem cadastro?

Outros materiais