Sei sulla pagina 1di 59

Hibernate conceitos e consultas

Jobson Ronan {jrjs@cin.ufpe.br}

Objetivos

Aprender sobre os conceitos de persistncia relacionados ao Hibernate Aprender como recuperar os dados e efetuar consultas de maneira eficientemente no Hibernate

Ciclo de vida da persistncia

Como Hibernate oferece um sistema transparente de persistncia, as classes no sabem de sua capacidade de persistncia Por outro lado, a aplicao que usa os objetos, os utiliza em estados diferentes

Transientes, antes de serem gravados em meio persistente Persistentes, quando esto armazenados Desligados, quando suas instncias so manipuladas sem afetar a base de dados

Diagrama de estados

Objetos transientes

Objetos que acabaram de ser criados (com new) ainda no so persistentes


Seu estado transiente (ainda no foram armazenados no banco e deixaro de existir assim que perderem sua referncia) Session.delete() sobre um objeto persistente torna-o transiente Rollback no recupera seu estado anterior

Instncias transientes so no-transacionais

Objetos referenciados por instncias transientes so (por default) tambm transientes Para mudar para o estado persistente preciso

Passar o objeto como argumento de um Session.save(), ou Criar a referncia a partir de uma instncia persistente

Objetos persistentes

Uma instncia persistente uma instncia com uma identidade no banco de dados

Tem uma chave primria como identificador Objeto criado com new e armazenado com Session.save() Objeto criado a partir da referncia de uma instncia persistente Objeto obtido a partir de um query no banco

Podem ser

Esto sempre associados com uma Session So transacionais


Seu estado sincronizado com o banco ao fim da transao Automatic dirty checking (transparente ao usurio) usado como estratgia de atualizao eficiente de dados

Objetos desligados (detached)

Quando a sesso fechada (Session.close()) todas as instncias ainda mantm seu estado, mas no esto mais sincronizadas com o banco

Podem ficar obsoletas se houver mudana no banco Podem mudar de estado, que no ser refletido no banco Mas, a qualquer momento, a sesso pode ser reativada, tornando o objeto persistente e sincronizando seu estado

Objetos desligados so teis para transportar o estado de objetos persistentes para outras camadas

Camada de apresentao, em substituio aos DTOs (Data Transfer Objects, tambm chamados de Value Objects)

Como tornar um objeto persistente

1) Crie o objeto e inicialize suas propriedades


User user = new User(); user.getName().setFirstname("John"); user.getName().setLastname("Doe");

2) Abra uma sesso


Session session = factory.openSession();

3) Inicie uma transao


Transaction tx = session.beginTransaction();

4) Grave o objeto
session.save(user);

5) Cometa a transao
tx.commit();

6) Feche a sesso
session.close();

Como atualizar estado de instncias desligadas

Quando a sesso for fechada, user torna-se uma instncia desligada


Qualquer alterao no seu estado no afeta o banco Para relig-lo pode-se usar update()

user.setPassword("secret"); // objeto desligado Session sessionTwo = sessions.openSession(); Transaction tx = sessionTwo.beginTransaction(); sessionTwo.update(user); user.setUsername("jonny"); // objeto persistente tx.commit(); sessionTwo.close();

Como recuperar um objeto persistente

A forma mais simples de recuperar um objeto pelo identificador, usando o comando get():
Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); int userID = 1234; User user = (User) session.get(User.class, new Long(userID)); tx.commit(); session.close();

Uma vez que a sesso foi fechada, o objeto uma instncia desligada, e pode ser repassada para outras camadas (apresentao, por exemplo) Se o objeto no existir, a chamada get() retorna null

Como atualizar um objeto persistente

Qualquer objeto retornado por get() um objeto persistente


Quaisquer modificaes no seu estado sero sincronizadas com o banco de dados Hibernate automaticamente verifica e grava as mudanas ocorridas dentro de uma sesso (automatic dirty checking) As mudanas tornam-se permanentes ao cometer a transao O objeto torna-se desligado quando a sesso fecha

Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); int userID = 1234; User user = (User) session.get(User.class, new Long(userID)); user.setPassword("secret"); tx.commit(); session.close();

Como tornar transiente um objeto persistente

Um objeto persistente pode tornar-se transiente se for removido do banco


Para isto preciso usar o gerente de persistncia e chamar o mtodo delete() Quando a sesso terminar o objeto ser considerado um mero objeto Java transiente (cujo estado ser perdido assim que o coletor de lixo atuar)

Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); int userID = 1234; User user = (User) session.get(User.class, new Long(userID)); session.delete(user); tx.commit(); session.close();

Como tornar transiente um objeto desligado

Para tornar um objeto desligado transiente, no preciso lig-lo ou fazer update() Basta chamar delete() sobre sua instncia
Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); session.delete(user); tx.commit(); session.close();

API Session: ciclo de vida (Resumida)

beginTransaction()

Demarca o incio de uma transao. Retorna um objeto Transaction que deve chamar commit() ou rollback() no final da transao. Fecha e desconecta a sesso Desconecta a sesso da conexo JDBC atual sem fech-la. Obtm uma nova conexo JDBC para a sesso ou tenta conectar a uma conexo JDBC, se passada como argumento Sincroniza a camada de objetos com a camada de dados. Este mtodo chamado automaticamente quando a transao cometida.

close()

disconnect()

reconnect()

flush()

Session: gerncia de persistncia (Resumida)


save(Object)

Torna persistente o objeto passado como argumento Insere (tornando persistente) ou atualiza o objeto passado como argumento Atualiza o objeto passado como argumento Remove os dados de um objeto do banco, tornando-o transiente Carrega e retorna a instncia de objeto identificado pela classe e identificador Recarrega do banco os dados da instncia passada como argumento Remove o objeto passado como argumento do cache do sistema

saveOrUpdate(Object)

update(Object)

delete(Object instancia)

load(classe, identificador)

refresh(Object)

evict(Object)

Recuperao de dados

Formas de recuperar objetos


Pelo identificador Navegando na rvore de objetos [ ex: usuario.getEndereco().getCidade() ] Usando HQL Usando a API Criteria Usando SQL nativo

Recuperao por identificador

H dois mtodos de Session para recuperar objetos pelo identificador

Ambos utilizam o cache, e evitam acessar o banco se no for necessrio Devolve o objeto se existir e null se o objeto no for encontrado no banco ou no cache Devolve o objeto ou um proxy para o objeto se existirem, ou causa exceo se nenhum for encontrado no banco ou no cache O proxy pode apontar para objeto que ainda ou no mais existe

Object get(Object id)

Object load(Object id)

HQL

Hibernate Query Language um dialeto orientado a objetos do SQL

Assemelha-se a ODMG OQL e EJB-QL mas adaptado para uso em bancos de dados SQL No uma linguagem de manipulao de dados (como SQL); no serve para inserir, remover, atualizar usada apenas para recuperao de objetos

Exemplo

Query q = session.createQuery("from User u where u.firstname = :fname"); q.setString("fname", "Max"); List result = q.list();

HQL

HQL suporta vrios recursos teis e avanados; entre eles


Pesquisas com navegao do grafo de objetos Recuperao seletiva de propriedades dos objetos selecionados (sem ter que carregar objeto inteiro) Ordenao de resultados Paginao de resultados Agregao com group by, having, e funes sobre agregados como sum, count, min e max Outer joins ao recuperar mltiplos objetos por registro Chamadas a funes SQL definidas pelo usurio Subqueries

Consulta usando Criteria (QBC)


QBC = Query By Criteria Queries podem ser expressos usando uma API em vez de usar HQL

Evita uso de strings Validao feita em tempo de compilao Enfoque mais orientado a objetos Mais difcil de ler ( Java e no HQL) Em HQL: from User u where u.firstname = :fname Em QBC:

Exemplo

Criteria criteria = session.createCriteria(User.class); criteria.add( Expression.like("firstname", "Max") ); List result = criteria.list();

Como achar os dados?


Talvez o problema mais difcil de solucionar em ORM: acesso eficiente a dados relacionais Aplicao prefere tratar os dados como um grafo de objetos interligados por associaes

mais fcil fazer vrios pequenos queries (performance baixa) Alternativa: escrever queries para cada associao (muito complexo e pouco flexvel)

Hibernate permite especificar uma estratgia de recuperao default (no mapeamento) que pode ser sobreposta em tempo de execuo

Estratgias de recuperao

Fetching strategies Estratgias para qualquer associao (nos metadados e em tempo de execuo)

Lazy fetching objeto associado s recuperado quando chamado Eager fetching objeto associado recuperado atravs de SQL outer join Batch fetching acessa uma coleo de objetos prdeterminada

Lazy fetching

Permite que apenas parte do grafo de objetos seja carregado inicialmente


O restante do grafo pode ser carregado medida em que for necessrio Vai requer mais chamadas ao banco posteriormente, mas pode ser que elas no sejam necessrias Pode ser otimizada com Batch Fetching

Deve ser a estratgia inicial para todas as associaes no arquivo de mapeamento

A estratgia pode depois ser sobreposta em tempo de execuo por estratgias mais vidas

LazyInitializationException

Erro comum, causado por inicializao lazy

A forma mais simples de evit-lo, realizar tudo dentro da sesso (o commit() sincroniza os dados e evita a inconsistncia) Sabendo-se do estado do objeto, os dados podem ser recuperados em outra sesso (quando voltar a ser persistente)

s = sessions.openSession(); User u = (User) s.createQuery("from User u where u.name=?).setString(userName).list().get(0); Map permissions = u.getPermissions(); s.connection().commit(); s.close(); Integer accessLevel = (Integer) permissions.get("accounts"); // Error!

Recuperao em lote (Batch fetching)

Na verdade no uma outra estratgia


uma soluo rpida para otimizar lazy fetching um lazy menos lazy: usurio define quantos nveis de associaes devem ser carregadas

Hibernate procura as outras instncias associadas coleo e tenta carreg-las ao mesmo tempo Pode ser melhor que lazy simples

Menos chamadas ao banco

Eager fetching

uma soluo que ajuda a reduzir a carga sobre o banco de dados de forma mais inteligente que Batch Fetching

Pode ser um a boa estratgia default

Permite que se especifique explicitamente quais objetos associados devem ser carregados juntos com o objeto que os referencia Os objetos associados podem ento ser retornados em nica requisio

Usa SQL outer join

Otimizao de performance em Hibernate freqentemente usa esse tipo de estratgia em tempo de execuo para um query em particular

Qual estratgia usar?

A seleo de uma estratgia de recuperao de dados default feita atravs de atributos no XML do arquivo de mapeamento. Mapeamentos de coleo diferem dependendo do tipo de associaes.

Colees e associaes de muitos para muitos comportam-se diferentemente de associaes singulares -to-one.

outer-join em associaes -to-one

O atributo outer-join sobrepe lazy fetching


Usado do lado oposto da associao O outro lado escolhe lazy fetching (ou immediate fetching por omisso) outer-join=auto (default). O comportamento lazy, se o outro lado (singular) da associao especificar, ou eager, se no especificado. outer-join=true Comportamento sempre eager para esta associao (logo diferentes associaes para mesmo objeto podem ter estratgias diferentes) outer-join=false Se o objeto tiver sido mapeado como lazy, ocorre lazy fetching, caso contrrio, immediate fetching (SQL select)

Opes

Exemplo:
<many-to-one name="item" class="Item" outer-join="true">

Em colees

Lazy

Use atributo lazy=true (no depende de proxy do outro lado)


<set name="bids" lazy="true">

Batched Lazy

Acrescente o atributo batch-size=tamanho (refere-se ao nmero de colees


<set name="bids" lazy="true" batch-size="9">

Eager

Use atributo outer-join=true Evite usar como opo default

Consultas HQL

O query mais simples possvel Os resultados de um query podem ser lidos em pginas O query abaixo l 10 pginas, a partir da primeira pgina
Query query = session.createQuery("from User u order by u.name asc"); query.setFirstResult(0); query.setMaxResults(10); List results = query.list();

Query q1 = session.createQuery("from User");


Listagem de resultados
List result = session.createQuery("from User").list();

Passagem de parmetros

Evite montar queries via concatenao de strings


Risco de segurana: basta esquecer de fechar a aspa Longos queries ficam ilegveis

O ideal passar parmetros que sero ligados ao query Parmetros podem ser passados duas formas

Por nome de varivel Por ordem (como PreparedStatement em java.sql)

Passagem de parmetros

Parmetros passados por nome

No query, o nome deve ser precedido de um :


String queryString = "from Item item where + item.description like :searchString";

Parmetros por posio


String queryString = "from Item item " + "where item.description like ? " + "and item.date > ?"; List result = session.createQuery(queryString) .setString(0, searchString) .setDate(1, minDate) .list();

Queries chamados pelo nome

Queries no precisam aparecer no cdigo


Na verdade, muitas vezes melhor que no apaream Podem ficar nos metadados e serem chamados pelo nome

Para chamar um query pelo nome, use getNamedQuery()


List result = session.getNamedQuery("findItemsByDescription") .setString("description", description).list();

Mas antes, preciso que ele seja declarado em algum arquivo de mapeamento (ex: Item.hbm.xml):
<query name="findItemsByDescription"><![CDATA[ from Item item where item.description like :description ]]></query>

Aliases

Aliases (apelidos) so usados para que se possa ter uma referncia para acessar propriedades das instncias recuperadas
from Livro

suficiente para recuperar todos os livros Um alias declarado da seguinte forma


from Livro as livro

Mas o as opcional
from Livro livro

Exemplo: para testar as propriedades em um where


from Livro livro where livro.codigo = 005.133

Queries polimrficos

Considere a hierarquia ao lado O query abaixo polimrfico


from BillingDetails

BillingDetails

pois os resultados so classes concretas CreditCard BankAccount BankAccount e CreditCard Queries polimrficos podem ser feitos em qualquer classe, no apenas em classes mapeadas O query abaixo retorna todas as instncias mapeadas
from java.lang.Object

Criteria tambm suporta polimorfismo


List res = session.createCriteria(java.lang.Object.class) .list();

Mais HQL: Restries (where)


Como em SQL, HQL define restries de um query atravs da clusula where, que suporta vrias expresses. Por exemplo, a expresso de equivalncia:
from User u where u.email = 'foo@hibernate.org

A expresso where pode ter resultado true, false ou null

lgica ternria

HQL suporta os mesmos operadores que SQL Queries Criteria: expresses na classe Expression

No h suporte para expresses aritmticas via API (s via HQL) Comparao: =, <>, <, >, >=, <=, between, not between, in, not in Nulidade: is null, is not null Aritmticos: +, -, /, *, %, parnteses

Operadores do HQL:

Mais HQL: Restries (where)

Exemplos
Bid bid where bid.amount between 1 and 10 Bid bid where bid.amount > 100 User u where u.email in ( "foo@abc.org", "bar@abc.org" ) User u where u.email is null Bid bid where ( bid.amount / 0.71 ) - 100.0 > 0.0

from from from from from

Mais HQL: strings

O operador like funciona da mesma forma que SQL


O smbolo _ representa um caractere O smbolo % representa vrios caracteres HQL: from User u where u.firstname like "G%" Criteria: session.createCriteria(User.class)

Exemplos

.add(Expression.like("firstname","G%"))

Pode-se usar funes SQL se banco suportar


from User u where lower(u.email) = 'foo@abc.org'

No h um operador padro de concatenao de strings


preciso usar recursos do banco nativo. Exemplo:

where (u.fname || ' ' || u.lname) like 'G% K%'

Mais HQL

Operadores lgicos: and, or, parnteses, etc. servem para argupar expresses
from User user where ( user.firstname like "G%" and user.lastname like "K%" ) or user.email in ( "foo@abc.org", "bar@abc.org" )

Ordenao: Semelhante a SQL: order by


asc ascendente (default) desc descendente

from User u order by u.lname asc, u.fname asc

...Pensou que acabou?

Joins

Joins (junes) so usados para combinar dados de duas ou mais tabelas relacionadas H quatro tipos de joins possveis

Inner join Left outer join Right outer join Full join ANSI: juno de tabelas atravs de propriedade comum com condio de join na clusula on Theta: produto cartesiano de tabelas com condio de join na clusula where

Alm disso, h dois estilos em SQL


Joins em HQL so bem mais simples que em SQL

SQL: ANSI inner join e left outer join


Inner join (ou simplesmente join) apenas Itens com Bids

Left outer join (ou left join) - Inclui tambm Itens que no tm Bids

Right outer join (right join) - permite valores nulos na tabela esquerda (inclui Bids sem Itens mas no itens sem Bids) no faz sentido neste caso (lance sem item) Full join - permite valores nulos nas duas tabelas (inclui Bids que no tm Itens e itens que no tm Bids) tambm no faz sentido neste caso

Joins em HQL

Em Hibernate, geralmente condies de join no so especificadas explicitamente

Elas podem ser deduzidas das associaes entre objetos automaticamente Queries ficam mais simples e legveis Fetch join (outer, agressivo) na clusula from Join comum (inner, lazy) na clusula from Join theta-style (produto cartesiano) na clusula where Join por associao explcita

Quatro maneiras de expressar joins em HQL


Fetch join

Recupera objeto inteiro (com associaes inicializadas.) Exemplo HQL: Retorna lista de
from Item item left join fetch item.bids where item.description like '%gc%
objetos com colees j inicializadas

Join simples (inner) com alias


Freqentemente necessrio aplicar restries a tabelas combinadas com join. Isto pode ser feito atribundo um alias ao join:
from Item item join item.bids bid where item.description like '%gc% and bid.amount > 100

O query acima um inner join


Em vez de uma lista de Item, retorna uma lista de Object[] Retorna uma lista de arrays {item, bid} (Se fosse um fetch join, retornaria uma lista de Item com coleo de bids inicializada) Um Item pode aparecer mltiplas vezes (uma vez para cada Bid associado)

inner join x outer (fetch) join

Como obter os dados usando um inner join


Query q = session.createQuery("from Item item join item.bids bid"); Iterator pairs = q.list().iterator(); Retorna pares while ( pairs.hasNext() ) { de referncias Object[] pair = (Object[]) pairs.next(); Apenas Items Item item = (Item) pair[0]; que tm Bid Bid bid = (Bid) pair[1]; }

E usando um left outer join com fetch


Query q = session.createQuery("from Item item left join fetch item.bids"); Retorna Items Iterator items = q.list().iterator(); while ( items.hasNext() ) { Todos os Items Item item = (Item) items.next(); inclusive os que Bid bid = (Bid) item.getBid(); no tm Bid }

Clusula select

Pode-se restringir os objetos retornados com uma clusula select


Select opcional em HQL (mas no em SQL) A ausncia do select equivale ao SQL select *

Para que o inner join do exemplo anterior devolva apenas os itens (e no uma lista de Object[] com par item-bid) pode-se usar select:
select item from Item item join item.bids bid

Exemplo

Query q = session.createQuery("select i from Item i join i.bids b"); Iterator items = q.list().iterator(); while ( items.hasNext() ) { Item item = (Item) items.next(); }

Agora temos uma lista de Items contendo apenas os Items que tm Bids (inner join)

Joins implcitos

Ocorrem em associaes *-para-um (caminho convergente) e nunca em caminhos *-para-muitos O HQL a seguir causa trs joins em SQL
from Bid bid where bid.item.category.name like 'Laptop%' and bid.item.successfulBid.amount > 100

E pode ser expresso explicitamente, usando


from Bid bid join bid.item item where item.category.name like 'Laptop% and item.successfulBid.amount > 100

Ou ainda
from Bid as bid join bid.item as item join item.category as cat join item.successfulBid as winningBid where cat.name like 'Laptop% and winningBid.amount > 100

Joins implcitos sendo revelados

Produtos cartesianos (theta style joins)


Permite recuperar todas as combinaes possveis de instncias de duas ou mais classes til em classes no associadas

Condio de join deve estar na clusula where Apenas inner-join pode ser usado neste caso

Produto cartesiano

Exemplo
Query query = session.createQuery("from User user, LogRecord log " + "where user.username = log.username") Iterator i = query.list().iterator(); while ( i.hasNext() ) { Object[] pair = (Object[]) i.next(); Condio de join User user = (User) pair[0]; LogRecord log = (LogRecord) pair[1]; }

Pode-se usar uma clusula select para filtrar os resultados

Comparao de identificadores

Queries que comparam chaves-primrias so realizados implicitamente em HQL

o mesmo que comparar instncias


from Item i, User u where i.seller = u and u.username = 'steve' from Item i, Bid b where i.seller = b.bidder from Item i, User u where i.seller.id = u.id and u.username = 'steve' from Item i, Bid b where i.seller.id = b.bidder.id Esta ltima sintaxe permite que se passe parmetros (tipo, o id) para comparao.

Os dois queries ....


So respectivamente equivalentes aos queries:


Queries de relatrios

Relatrios geralmente focam nos dados

Instncias no so importantes, mas valores selecionados, agrupados e organizados de certas instncias Dependem de recursos de seleo e agrupamento Usa-se clusulas select e group by / having.

Projeo: clusula select


Quais os dados desejados no resultado? O query abaixo seleciona trs propriedades de dois objetos

Os dados sero retornados em um vetor de Object[] de tamanho 3. Os resultados no so entidades (so valores) e no so transacionais (sero usados para leitura, apenas)

Query query = session.createQuery("select item.id, item.description, bid.amount " + "from Item item join item.bids bid where bid.amount > 100"); Iterator i = query.list().iterator(); while ( i.hasNext() ) { Object[] row = (Object[]) i.next(); Long id = (Long) row[0]; String description = (String) row[1]; BigDecimal amount = (BigDecimal) row[2]; ItemRow itemRow = new ItemRow(id, description, amount); }

Instanciamento dinmico

Hibernate permite o instanciamento dinmico de objetos que so povoados pelos resultados do query

Evita o uso de vetores de objetos necessrio que exista uma classe e construtor previamente construda

class ItemRow { public ItemRow(Long id, String description, BigDecimal amount) {...} ... } Query query = session.createQuery( "select new ItemRow(item.id, item.description, bid.amount)" + "from Item item join item.bids bid where bid.amount > 100"); Iterator i = query.list().iterator(); while ( i.hasNext() ) { ItemRow row = (ItemRow) i.next(); // ... fazer alguma coisa }

Distinct

Com o uso da clusula select, os resultados de um query no mais tem garantia de serem distintos A seguinte query pode retornar Items repetidos
select item.description from Item item

Para evitar duplicao, use a clusula distinct


select distinct item.description from Item item

Funes agregadas

HQL suporta as seguintes funes na clusula select


count() retorna Integer min(), max(), sum() e avg() retornam BigDecimal

Integer count = (Integer) session.createQuery("select count(*) from Item").uniqueResult(); BigDecimal sum = (BigDecimal) session.createQuery("select sum(item.successfulBid.amount) " +"from Item item").uniqueResult(); BigDecimal[] minmax = (BigDecimal[]) session.createQuery("select min(bid.amount), max(bid.amount) +"from Bid bid where bid.item.id = 1") .uniqueResult(); BigDecimal min = minmax[0]; BigDecimal max = minmax[1];

Agrupamento

Se houver funes agregadas no select, no pode haver seleo de outros elementos, a menos que haja uma clusula group by

Neste caso, os elementos devero estar tambm no group by


Um inner join implcito

select bid.item.id, count(bid), avg(bid.amount) from Bid bid where bid.item.successfulBid is null group by bid.item.id select bidItem.id, count(bid), avg(bid.amount) from Bid bid join bid.item bidItem where bidItem.successfulBid is null group by bidItem.id select item.id, count(bid), avg(bid.amount) from Item item fetch join item.bids bid where item.successfulBid is null group by item.id

Um inner join explcito

Um fetch (outer) join; o agrupamento teve que ser feito usando item.id

Restrio de grupos com having

Pode-se aplicar restries no agrupamento com a clusula having


select user.lastname, count(user) from User user group by user.lastname having user.lastname like 'A%

Grupos requerem o uso de pelo menos dois aliases no select (a funo e a propriedade de agrupamento) Sempre que a clusula select tem mltiplos aliases, o Hibernate devolve o resultado como (tuplas)vetores de Object

Chato de manipular; inseguro quanto ao tipo Ideal usar select new e instanciar objetos dinamicamente

Agrupamento com instanciamento dinmico


select new UsuarioReport(user.lastname, count(user)) from User user group by user.lastname having user.lastname like 'A%

...agora acabo!

Referncias

Hibernate Reference Documentation Hibernate in Action

Potrebbero piacerti anche