Hibernate Java avançado – PCC Jobson Ronan {jrjs@cin.ufpe.br} Guilherme Kelly {gkmo@cin.ufpe.br}

Post on 19-Apr-2015

131 views 0 download

Transcript of Hibernate Java avançado – PCC Jobson Ronan {jrjs@cin.ufpe.br} Guilherme Kelly {gkmo@cin.ufpe.br}

Hibernate

Java avançado – PCCJobson Ronan {jrjs@cin.ufpe.br}

Guilherme Kelly {gkmo@cin.ufpe.br}

O que é?

Hibernate é uma moderna solução de mapeamento objeto-realcional(ORM) Persistência tranparente (POJO/Java

Beans) “Lazy Fetching” Uso de uma Cache Três estratégias para o mapeamento de

heranças

O que é?

Pra que transparência?

Persistência sem impacto no código dos objetos de negócio

Qualquer classe pode ser uma classe persistente sem ser necessário implementar nenhuma classe ou interface

Classes persistentes podem ser usadas fora do contexto de persistência (ex Testes)

Total portabilidade sem dependências

Problemas dos BDRs

Modelagem Não há polimorfismo Não há herança

Lógica de negócio Stored procedures -> perca de

portabilidade

Vantagens dos RDBs

Trabalhar com grandes quantidades de dados Busca, ordenação

Trabalhar com conjuntos de dados Junções e agregações

Compartilhamento Concorrência (Transações) Muitas aplicações

Integridade Restrições (Constraints) Isolação de transações

Obviamente, ainda precisamos dos RDBs

Objetivo

Aproveitar-se das vantagens oferecidas pelos RDBs

Isso sem perder a orientação a objetos

Objetivo Real

Ter menos trabalho Ter um DBA feliz

Em prática

Locadora em partes...

Em prática

Classe Persistente Construtor default Pares de Get´s e

Set´s Uma propriedade

identificadora

package br.org.citi.pec.locadora;

public class Filme { private int codigo; private String nome; public int getCodigo() { return codigo; }

private void setCodigo(int codigo) { this.codigo = codigo; }

public String getNome() { return nome; }

private void setNome(int nome) { this.nome = nome; }}

Em prática

Mapeamento XML Metadado legível Mapeamento de tabelas e colunas<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping> <class name="br.org.citi.pec.locadora.Filme" table="filmes"> <id name="codigo" column="filme_id"> <generator class="native" /> </id>

<property name="nome" column="nome"/>

</class></hibernate-mapping>

Em prática

Salvando um objeto

Session session = sessionFactory.openSession();

Transaction tx = s.beginTransaction();

Filme filme = new Filme();

filme.setNome(novoNome);

session.save(filme);

tx.commit();

session.close();

Também pode ser usado saveOrUpdate

Em prática

Carregando um objeto

Session session = sessionFactory.openSession();

Filme filme = (Filme) session.load(Filme.class,

new Integer(filmeId);

session.close();

Não use load para determinar se um objeto existe (uma exceção é lançada neste caso)

Use get. Este retorna null caso o objeto não exista

Em prática

Removendo um objeto

Session session = sessionFactory.openSession();

Transaction tx = s.beginTransaction();

Filme filme = (Filme) session.get(Filme.class,

new Integer(filmeId);

session.delete(filme);

tx.commit();

session.close();

Em prática

Atualizando um objeto (Dirty Checking) Obetendo um filme e alterando seu nome

Session session = sessionFactory.openSession();

Transaction tx = s.beginTransaction();

Filme filme = (Filme) session.get(Filme.class,

new Integer(filmeId);

filme.setNome(novoNome);

tx.commit();

session.close();

Não é necessário uma chamada explicita do update

Estado dos Objetos

Alguns conceitos sobre objetos Transiente.

• Nunca persistido. Não associado a nenhuma sessão (Session)

Persistente

• Associado a uma única sessão

Desacoplado (Detached)

• Já persistido, mas não associado a nenhuma sessão

Estado dos Objetos

Dirty Checking só funciona em objetos persistentes Use update para objetos desacoplados

Mais prática

Melhorando nosso modelo...

Mais prática Classes persistentes

package br.org.citi.pec.locadora;

public class Cliente { private String login; private String CPF; private String nome; private Set locacoes = new Hashset();

//... Get´s e Set´s}

Mais prática

package br.org.citi.pec.locadora;

public class Locacao { private int id;

private Filme filme; private Cliente cliente;

private Date dataLocacao; private Date datadevolucao;

//... Get´s e Set´s}

Classes persistentes

Mais prática XML...

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping> <class name="br.org.citi.pec.locadora.Cliente" table="Clientes"> <id name="login"/>

<property name="nome" /> <property name="cpf" />

<set name="locacoes"> <key column="cliente_login" /> <one-to-many class="br.org.citi.pec.locadora.Locacao"/> </set> </class></hibernate-mapping>

Mais prática

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping> <class name="br.org.citi.pec.locadora.Locacao" table="Locacoes"> <id name="id"> <generator class="native" /> </id>

<property name="dataLocacao" /> <property name="dataDevolucao" />

<many-to-one name="cliente" class="br.org.citi.pec.locadora.Cliente" column="cliente_login"/> <many-to-one name="filme" class="br.org.citi.pec.locadora.Filme" column="filme_id"/> </class></hibernate-mapping>

XML...

Mais prática Persistência transitiva

Locacao locacao = new Locacao();

Locacao.setDataLocacao(new Date());

Session session = sessionFactory.openSession();

Transaction tx = s.beginTransaction();

Filme filme = (Filme)

session.get(Filme.class, new Integer(filmeId);

Cliente cliente = (Cliente)

session.get(Cliente.class, login);

locacao.setCliente(cliente);

locacao.setFilme(filme);

cliente.getLocacoes().add(locacao);

tx.commit();

session.close();

Otimizando

Como as coleções são carregadas Lazy fetching (Default) Eager (Outer Join) fetching

• Indicado nos XML, ou na própria consulta

Lazy fetching

Cliente cliente = (Cliente) session.get(Cliente.class, login);

SELECT … FROM CLIENTES C WHERE C.LOGIN = ?

Iterator cliente = cliente.getLocacoes().iterate();

SELECT … FROM LOCACOES L WHERE L.LOGIN_CLIENTE = ?

Filme filme = locacao.getFilme();

SELECT … FROM FILMES F WHERE F.FILME_ID = ?

SQL escondido:

Outer join fetching SQL escondido:

Cliente cliente = (Cliente) session.get(Cliente.class, login);

SELECT … FROM CLIENTES CLEFT OUTER JOIN LOCACOES L ON L.LOGIN_CLIENTE = C.LOGIN

LEFT OUTER JOIN FILME F ON L.FILME_ID = F.FILME_ID

WHERE C.LOGIN = ?

Herança

Estratégias para o mapeamento de herança Uma tabela por hierarquia de classes Uma tabela por subclasse Uma tabela por classe concreta

Herança Uma tabela por subclasse

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping> <class name="br.org.citi.pec.locadora.Cliente" table="Clientes"> <id name="login" column="LOGIN" />

... <joined-subclass

name="br.org.citi.pec.locadora.ClienteEspecial" table="ClientesEspeciais">

<key column="LOGIN" /> <property ... /> ... </joined-subclass>

</class></hibernate-mapping>

Otimizando

Como otimizar? Minimizando a leitura de linhas das

tabelas Minimizando a quantidade de comandos

SQLs enviados

Otimizando

Minimizando a leitura de linhas das tabelas Usar lazy fecthing

Minimizando a quantidade de comandos SQLs enviados Usar outer join fetching

Otimizando

Problemas Usar lazy fecthing

• Problema dos n+1 selects (muitos selects!)

Usar outer join fetching• Problema do produto cartesiano (grandes

conjuntos de dados)

Otimizando

Solucionando problemas de otimização Stratégia de Fecthing definida em

tempo de execução Batch Fecthing Utilizar um cache de segundo nível

Consultas

HIbernate Query Lanuage Permite que se expresse quase tudo o

que se precisa expressar em SQL, porém mais orientado a objetos

Três maneiras de se fazer consultas no Hibernate HQL Criteria SQL nativo

HQL

Tornar SQL orientado a objetos Classes e propriedades ao invés de Tabelas e colunas Polimorfismo Associações

Total suporte a operações relacionais Joins Projeções Funções agregadas e agrupamento Ordenação SubQueries Chamadas a funções SQL

HQL

A consulta mais simples possivel HQL

from Filme

Devolva todos os filmes

List filmes = session.createQuery(“from Filme”).list();

As consultas podem ser paginadas

Query query = session.createQuery(“from Filme”);query.setFirstResult(20);query.setMaxResults(30);

List filmes = query.list();

HQL

Uma consulta com ordenação

from Locacao l order by l.dataLocacao desc

Caso esteja interessado em um único resultado

Query query = session.createQuery(“from Locacao l order by l.dataLocacao desc”);

Locacao locacao = query.uniqueResult();

HQL

Uma consulta com joins

select c from Cliente c left join [fetch] c.locacaoes l

where l.dataLocacao > 3/5/2005

Todos os clientes com suas locações efetuadas após o dia 3

Definição da estratégia de fetching Com o fetch: Coleções já inicializadas Sem o fetch: lazy collections

HQL

HQL suporta os mesmos operadores que SQL

Operadores do HQL: Comparação: =, <>, <, >, >=, <=, between, not

between, in, not in

Nulidade: is null, is not null

Aritméticos: +, -, /, *, %, parênteses

O operador like funciona da mesma forma que SQL

Pode usar funções SQL se o banco suportar

HQL

Projeções + funções agregadas (Report Queries)

select c.nome, count(l)

from Cliente c, c.locacoes l

where l.dataLocacao between(:inicio, :fim)

group by c.nome

order by count(l)

HQL

Os dados da consulta anterior serão retornados em List de Object[]

Consulta comum para a realização de relatórios

Podemos tornar o processo mais elegante

List list = session.createQuery( “select new ClienteReportRow(c.nome, count(l)) ” +

“from Cliente c, c.locacoes l ” + “where l.dataLocacao between(:inicio, :fim) ” + “group by c.nome ” + “order by count(l)”). setDate(“inicion”, inicio). setDate(“fim”, fim).list();

HQL

Dessa evitasse o uso de vetores de objetos

É necessário que exista uma classe e um construtor previamente definido

HQL

Consultas não precisam aparecer no código Na verdade, muitas vezes é melhor que não apareçam Podem ficar nos metadados e serem chamadas pelo

nome Usa-se o método getNamedQuery()

Mas antes, ela precisa ser declarada em algum arquivo de mapeamento

List clientes = session.getNamedQuery(“findClienteByName”)

.setString(“nome”, nome),list()

<query name=“findClienteByName”><![CDATA[from Cliente c where c.nome like :nome]]

</query>

Modelagem OO

“Mais classes que tabelas” Classse Endereco

• Propriedade: rua, numero, bairro... Rua, numero, bairro,... Colunas da

tabela cliente<class name="br.org.citi.pec.locadora.Cliente"

table="Clientes">

<component name=“endereco” class=“br.org.citi.pec.locadora.Endereco”>

<property name=“rua” column=“rua”/>

<property name=“numero” column=“numero”/>

</component>

</class>

O que mais?

Hibernate também dá suporte a tipos de dados definidos pelo usuário

Interceptadores e EventListeners Filtros Definições explicitas dos SQLs de

insert e update e load (hibernate 3)

...e mais um pouco!

Sobre configuração

Como obter um SessionFactory? Programaticamente Por arquivo XML

Configuração programática

Configuration cfg = new Configuration().setProperty("hibernate.dialect",

"org.hibernate.dialect.HSQLDialect")..setProperty("hibernate.connection.driver_class",

"org.hsqldb.jdbcDriver").setProperty("hibernate.connection.url",

"jdbc:hsqldb:file:hsqldb/data").setProperty("hibernate.connection.username", "sa").setProperty("hibernate.connection.password", "").addClass(Filme.class).addClass(Cliente.class).addClass(Locacao.class);

SessionFactory sessionFactory = cfg.buildSessionFactory();

Configuração XML

<?xml version="1.0" encoding="UTF-8"?><!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.MySQLDialect</property> <property name="hibernate.connection.driver_class">

org.gjt.mm.mysql.Driver</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">ftBBvEdiC</property> <property name="hibernate.connection.url">

jdbc:mysql://localhost/lockar</property> <mapping class="br.org.citi.pec.locadora.Filme"/> <mapping class="br.org.citi.pec.locadora.Locacao"/> <mapping class="br.org.citi.pec.locadora.Cliente"/> </session-factory></hibernate-configuration>

hibernate.cfg.xml

Configuration cfg = new Configuration().configure();SessionFactory sessionFactory = cfg.buildSessionFactory();

Extras

Hbm2ddl (tool) Gera o schema do BD a partir das XML

de mapeamentos Usado por linha de comando ou pelo ant

<target name="schemaexport"> <taskdef name="schemaexport"

classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"classpathref="project.lib.classpath" />

<schemaexport properties="hibernate.properties"

quiet="yes" text="yes" drop="no" delimiter=";"output="${basedir}/sql/schema-export.sql">

<fileset dir="src"> <include name="**/*.hbm.xml" /> </fileset> </schemaexport></target>

Exercícios

Ampliar nosso modelo

Exercícios

Crie as classes (JavaBeans/POJO) Crie os XML de mapeamentos Implemente as consultas e métodos para retornar

Todos os filmes locados durante um determinado mês Listagem contendo nome do filme, média das avaliações, e

total de comentários para todos os filmes Listagem contendo nome do filme mais o número de cópias de

VHS e de DVD desse filme Listagem contendo nome do filme mais o número de cópias de

VHS e de DVD desse filme locadas em um determinado mês Todos os clientes que locaram mais de 5 VHS no ultimo mês

(Com as devidas locações carregadas)