1 / 59

Hibernate – conceitos e consultas

Hibernate – conceitos e consultas. Jobson Ronan {jrjs@cin.ufpe.br}. Objetivos. Aprender sobre os conceitos de persistência relacionados ao Hibernate Aprender como recuperar os dados e efetuar consultas de maneira eficientemente no Hibernate. Ciclo de vida da persistência.

rufina
Download Presentation

Hibernate – conceitos e consultas

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Hibernate – conceitos e consultas Jobson Ronan {jrjs@cin.ufpe.br}

  2. Objetivos • Aprender sobre os conceitos de persistência relacionados ao Hibernate • Aprender como recuperar os dados e efetuar consultas de maneira eficientemente no Hibernate

  3. Ciclo de vida da persistência • Como Hibernate oferece um sistema transparente de persistência, as classes não sabem de sua capacidade de persistência • Por outro lado, a aplicação que usa os objetos, os utiliza em estados diferentes • Transientes, antes de serem gravados em meio persistente • Persistentes, quando estão armazenados • Desligados, quando suas instâncias são manipuladas sem afetar a base de dados

  4. Diagrama de estados

  5. Objetos transientes • Objetos que acabaram de ser criados (com new) ainda não são persistentes • Seu estado é transiente (ainda não foram armazenados no banco e deixarão de existir assim que perderem sua referência) • Session.delete() sobre um objeto persistente torna-o transiente • Instâncias transientes são não-transacionais • Rollback não recupera seu estado anterior • Objetos referenciados por instâncias transientes são (por default) também transientes • Para mudar para o estado persistente é preciso • Passar o objeto como argumento de um Session.save(), ou • Criar a referência a partir de uma instância persistente

  6. Objetos persistentes • Uma instância persistente é uma instância com uma identidade no banco de dados • Tem uma chave primária como identificador • Podem ser • Objeto criado com new e armazenado com Session.save() • Objeto criado a partir da referência de uma instância persistente • Objeto obtido a partir de um query no banco • Estão sempre associados com uma Session • São transacionais • Seu estado é sincronizado com o banco ao fim da transação • Automatic dirty checking (transparente ao usuário) é usado como estratégia de atualização eficiente de dados

  7. Objetos desligados (detached) • Quando a sessão é fechada (Session.close()) todas as instâncias ainda mantém seu estado, mas não estão mais sincronizadas com o banco • Podem ficar obsoletas se houver mudança no banco • Podem mudar de estado, que não será refletido no banco • Mas, a qualquer momento, a sessão pode ser reativada, tornando o objeto persistente e sincronizando seu estado • Objetos desligados são úteis para transportar o estado de objetos persistentes para outras camadas • Camada de apresentação, em substituição aos DTOs (Data Transfer Objects, também chamados de Value Objects)

  8. 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 sessão Session session = factory.openSession(); • 3) Inicie uma transação Transaction tx = session.beginTransaction(); • 4) Grave o objeto session.save(user); • 5) Cometa a transação tx.commit(); • 6) Feche a sessão session.close();

  9. Como atualizar estado de instâncias desligadas • Quando a sessão for fechada, user torna-se uma instância desligada • Qualquer alteração no seu estado não 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();

  10. Como recuperar um objeto persistente • A forma mais simples de recuperar um objeto é pelo identificador, usando o comando get(): • Uma vez que a sessão foi fechada, o objeto é uma instância desligada, e pode ser repassada para outras camadas (apresentação, por exemplo) • Se o objeto não existir, a chamada get() retorna null 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();

  11. Como atualizar um objeto persistente • Qualquer objeto retornado por get() é um objeto persistente • Quaisquer modificações no seu estado serão sincronizadas com o banco de dados • Hibernate automaticamente verifica e grava as mudanças ocorridas dentro de uma sessão (automatic dirty checking) • As mudanças tornam-se permanentes ao cometer a transação • O objeto torna-se desligado quando a sessão 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();

  12. 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 persistência e chamar o método delete() • Quando a sessão 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();

  13. Como tornar transiente um objeto desligado • Para tornar um objeto desligado transiente, não é preciso ligá-lo ou fazer update() • Basta chamar delete() sobre sua instância Session session = sessions.openSession(); Transaction tx = session.beginTransaction(); session.delete(user); tx.commit(); session.close();

  14. API Session: ciclo de vida (Resumida) • beginTransaction() • Demarca o início de uma transação. Retorna um objeto Transaction que deve chamar commit() ou rollback() no final da transação. • close() • Fecha e desconecta a sessão • disconnect() • Desconecta a sessão da conexão JDBC atual sem fechá-la. • reconnect() • Obtém uma nova conexão JDBC para a sessão ou tenta conectar a uma conexão JDBC, se passada como argumento • flush() • Sincroniza a camada de objetos com a camada de dados. Este método é chamado automaticamente quando a transação é cometida.

  15. Session: gerência de persistência (Resumida) • save(Object) • Torna persistente o objeto passado como argumento • saveOrUpdate(Object) • Insere (tornando persistente) ou atualiza o objeto passado como argumento • update(Object) • Atualiza o objeto passado como argumento • delete(Object instancia) • Remove os dados de um objeto do banco, tornando-o transiente • load(classe, identificador) • Carrega e retorna a instância de objeto identificado pela classe e identificador • refresh(Object) • Recarrega do banco os dados da instância passada como argumento • evict(Object) • Remove o objeto passado como argumento do cache do sistema

  16. Recuperação 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

  17. Recuperação por identificador • Há dois métodos de Session para recuperar objetos pelo identificador • Ambos utilizam o cache, e evitam acessar o banco se não for necessário • Object get(Object id) • Devolve o objeto se existir e null se o objeto não for encontrado no banco ou no cache • Object load(Object id) • Devolve o objeto ou um proxy para o objeto se existirem, ou causa exceção se nenhum for encontrado no banco ou no cache • O proxy pode apontar para objeto que ainda ou não mais existe

  18. 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 • Não é uma linguagem de manipulação de dados (como SQL); não serve para inserir, remover, atualizar • É usada apenas para recuperação de objetos • Exemplo Query q = session.createQuery("from User u where u.firstname = :fname"); q.setString("fname", "Max"); List result = q.list();

  19. HQL • HQL suporta vários recursos úteis e avançados; entre eles • Pesquisas com navegação do grafo de objetos • Recuperação seletiva de propriedades dos objetos selecionados (sem ter que carregar objeto inteiro) • Ordenação de resultados • Paginação de resultados • Agregação com group by, having, e funções sobre agregados como sum, count, min e max • Outer joins ao recuperar múltiplos objetos por registro • Chamadas a funções SQL definidas pelo usuário • Subqueries

  20. Consulta usando Criteria (QBC) • QBC = Query By Criteria • Queries podem ser expressos usando uma API em vez de usar HQL • Evita uso de strings • Validação é feita em tempo de compilação • Enfoque mais orientado a objetos • Mais difícil de ler (é Java e não HQL) • Exemplo • Em HQL: from User u where u.firstname = :fname • Em QBC: Criteria criteria = session.createCriteria(User.class); criteria.add( Expression.like("firstname", "Max") ); List result = criteria.list();

  21. Como achar os dados? • Talvez o problema mais difícil de solucionar em ORM: acesso eficiente a dados relacionais • Aplicação prefere tratar os dados como um grafo de objetos interligados por associações • É mais fácil é fazer vários pequenos queries (performance baixa) • Alternativa: escrever queries para cada associação (muito complexo e pouco flexível) • Hibernate permite especificar uma estratégia de recuperação default (no mapeamento) que pode ser sobreposta em tempo de execução

  22. Estratégias de recuperação • Fetching strategies • Estratégias para qualquer associação (nos metadados e em tempo de execução) • Lazy fetching – objeto associado só é recuperado quando chamado • Eager fetching – objeto associado é recuperado através de SQL outer join • Batch fetching – acessa uma coleção de objetos pré-determinada

  23. Lazy fetching • Permite que apenas parte do grafo de objetos seja carregado inicialmente • O restante do grafo pode ser carregado à medida em que for necessário • Vai requer mais chamadas ao banco posteriormente, mas pode ser que elas não sejam necessárias • Pode ser otimizada com Batch Fetching • Deve ser a estratégia inicial para todas as associações no arquivo de mapeamento • A estratégia pode depois ser sobreposta em tempo de execução por estratégias mais ávidas

  24. LazyInitializationException • Erro comum, causado por inicialização lazy • A forma mais simples de evitá-lo, é realizar tudo dentro da sessão (o commit() sincroniza os dados e evita a inconsistência) • Sabendo-se do estado do objeto, os dados podem ser recuperados em outra sessão (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!

  25. Recuperação em lote (Batch fetching) • Na verdade não é uma outra estratégia • É uma solução rápida para otimizar lazy fetching • É um lazy menos lazy: usuário define quantos níveis de associações devem ser carregadas • Hibernate procura as outras instâncias associadas à coleção e tenta carregá-las ao mesmo tempo • Pode ser melhor que lazy simples • Menos chamadas ao banco

  26. Eager fetching • É uma solução que ajuda a reduzir a carga sobre o banco de dados de forma mais inteligente que Batch Fetching • Pode ser um a boa estratégia default • Permite que se especifique explicitamente quais objetos associados devem ser carregados juntos com o objeto que os referencia • Os objetos associados podem então ser retornados em única requisição • Usa SQL outer join • Otimização de performance em Hibernate freqüentemente usa esse tipo de estratégia em tempo de execução para um query em particular

  27. Qual estratégia usar? • A seleção de uma estratégia de recuperação de dados default é feita através de atributos no XML do arquivo de mapeamento. • Mapeamentos de coleção diferem dependendo do tipo de associações. • Coleções e associações de muitos para muitos comportam-se diferentemente de associações singulares “-to-one”.

  28. outer-join em associações “-to-one” • O atributo outer-join sobrepõe lazy fetching • Usado do lado oposto da associação • O outro lado escolhe lazy fetching (ou immediate fetching por omissão) • Opções • outer-join=“auto”(default). O comportamento é lazy, se o outro lado (singular) da associação especificar, ou eager, se não especificado. • outer-join=“true”Comportamento é sempre eager para esta associação (logo diferentes associações para mesmo objeto podem ter estratégias diferentes) • outer-join=“false”Se o objeto tiver sido mapeado como lazy, ocorre lazy fetching, caso contrário, immediate fetching (SQL select) • Exemplo: <many-to-one name="item" class="Item" outer-join="true">

  29. Em coleções • Lazy • Use atributo lazy=“true” (não depende de proxy do outro lado) <set name="bids" lazy="true"> • Batched Lazy • Acrescente o atributo batch-size=“tamanho” (refere-se ao número de coleções <set name="bids" lazy="true" batch-size="9"> • Eager • Use atributo outer-join=“true” • Evite usar como opção default

  30. Consultas HQL • O query mais simples possível • Os resultados de um query podem ser lidos em “páginas” • O query abaixo lê 10 páginas, a partir da primeira página • Listagem de resultados Query q1 = session.createQuery("from User"); Query query = session.createQuery("from User u order by u.name asc");query.setFirstResult(0);query.setMaxResults(10);List results = query.list(); List result = session.createQuery("from User").list();

  31. Passagem de parâmetros • Evite montar queries via concatenação de strings • Risco de segurança: basta esquecer de fechar a aspa • Longos queries ficam ilegíveis • O ideal é passar parâmetros que serão ligados ao query • Parâmetros podem ser passados duas formas • Por nome de variável • Por ordem (como PreparedStatement em java.sql)

  32. Passagem de parâmetros • Parâmetros passados por nome • No query, o nome deve ser precedido de um “:” • Parâmetros por posição String queryString = "from Item item where “ + “item.description like:searchString"; String queryString = "from Item item " + "where item.description like? " + "and item.date >?"; List result = session.createQuery(queryString) .setString(0, searchString) .setDate(1, minDate) .list();

  33. Queries chamados pelo nome • Queries não precisam aparecer no código • Na verdade, muitas vezes é melhor que não apareçam • Podem ficar nos metadados e serem chamados pelo nome • Para chamar um query pelo nome, use getNamedQuery() • Mas antes, é preciso que ele seja declarado em algum arquivo de mapeamento (ex: Item.hbm.xml): List result = session.getNamedQuery("findItemsByDescription") .setString("description", description).list(); <query name="findItemsByDescription"><![CDATA[from Item item where item.description like :description]]></query>

  34. Aliases • Aliases (apelidos) são usados para que se possa ter uma referência para acessar propriedades das instâncias 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 livrowhere livro.codigo = ‘005.133’

  35. BillingDetails CreditCard BankAccount Queries polimórficos • Considere a hierarquia ao lado • O query abaixo é polimórfico from BillingDetails pois os resultados são classes concretas BankAccount e CreditCard • Queries polimórficos podem ser feitos em qualquer classe, não apenas em classes mapeadas • O query abaixo retorna todas as instâncias mapeadas from java.lang.Object • Criteria também suporta polimorfismo List res = session.createCriteria(java.lang.Object.class) .list();

  36. Mais HQL: Restrições (where) • Como em SQL, HQL define restrições de um query através da cláusula where, que suporta várias expressões. • Por exemplo, a expressão de equivalência: from User u where u.email = 'foo@hibernate.org‘ • A expressão where pode ter resultado true, false ou null • lógica ternária • HQL suporta os mesmos operadores que SQL • Queries Criteria: expressões na classe Expression • Não há suporte para expressões aritméticas via API (só via HQL) • Operadores do HQL: • Comparação: =, <>, <, >, >=, <=, between, not between, in, not in • Nulidade: is null, is not null • Aritméticos: +, -, /, *, %, parênteses

  37. Mais HQL: Restrições (where) • Exemplos from Bid bid where bid.amount between 1 and 10 from Bid bid where bid.amount > 100 from User u where u.email in ( "foo@abc.org", "bar@abc.org" ) from User u where u.email is null from Bid bid where ( bid.amount / 0.71 ) - 100.0 > 0.0

  38. Mais HQL: strings • O operador like funciona da mesma forma que SQL • O símbolo _ representa um caractere • O símbolo % representa vários caracteres • Exemplos • HQL: from User u where u.firstname like "G%" • Criteria: session.createCriteria(User.class) .add(Expression.like("firstname","G%")) • Pode-se usar funções SQL se banco suportar from User u where lower(u.email) = 'foo@abc.org' • Não há um operador padrão de concatenação de strings • É preciso usar recursos do banco nativo. • Exemplo: where (u.fname || ' ' || u.lname) like 'G% K%'

  39. Mais HQL • Operadores lógicos: and, or, parênteses, etc. servem para argupar expressões • Ordenação: Semelhante a SQL: order by • asc – ascendente (default) • desc – descendente from User user where ( user.firstname like "G%" and user.lastname like "K%" ) or user.email in ( "foo@abc.org", "bar@abc.org" ) from User u order by u.lname asc, u.fname asc

  40. ...Pensou que acabou?

  41. Joins • Joins (junções) são usados para combinar dados de duas ou mais tabelas relacionadas • Há quatro tipos de joins possíveis • Inner join • Left outer join • Right outer join • Full join • Além disso, há dois estilos em SQL • ANSI: junção de tabelas através de propriedade comum com condição de join na cláusula on • Theta: produto cartesiano de tabelas com condição de join na cláusula where • Joins em HQL são bem mais simples que em SQL

  42. SQL: ANSI inner join e left outer join • Inner join (ou simplesmente “join”) - apenas Itens com Bids • Right outer join (“right join”) - permite valores nulos na tabela esquerda (inclui Bids sem Itens mas não itens sem Bids) – não faz sentido neste caso (lance sem item) • Full join - permite valores nulos nas duas tabelas (inclui Bids que não têm Itens e itens que não têm Bids) – também não faz sentido neste caso • Left outer join (ou “left join”) - Inclui também Itens que não têm Bids

  43. Joins em HQL • Em Hibernate, geralmente condições de join não são especificadas explicitamente • Elas podem ser deduzidas das associações entre objetos automaticamente • Queries ficam mais simples e legíveis • Quatro maneiras de expressar joins em HQL • Fetch join (outer, agressivo) na cláusula from • Join comum (inner, lazy) na cláusula from • Join theta-style (produto cartesiano) na cláusula where • Join por associação explícita

  44. Fetch join • Recupera objeto inteiro (com associações inicializadas.) • Exemplo HQL: from Item item left join fetch item.bids where item.description like '%gc%‘ Retorna lista de objetos com coleções já inicializadas

  45. Join simples (inner) com alias • Freqüentemente é necessário aplicar restrições a tabelas combinadas com join. • Isto pode ser feito atribuíndo 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 coleção de bids inicializada) • Um Item pode aparecer múltiplas vezes (uma vez para cada Bid associado)

  46. 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(); while ( pairs.hasNext() ) { Object[] pair = (Object[]) pairs.next(); Item item = (Item) pair[0]; Bid bid = (Bid) pair[1]; } Retorna pares de referências Apenas Items que têm Bid • E usando um left outer join com fetch Query q = session.createQuery("from Item item leftjoinfetch item.bids"); Iterator items = q.list().iterator(); while ( items.hasNext() ) { Item item = (Item) items.next(); Bid bid = (Bid) item.getBid(); } Retorna Items Todos os Items inclusive os que não têm Bid

  47. Cláusula select • Pode-se restringir os objetos retornados com uma cláusula select • Select é opcional em HQL (mas não em SQL) • A ausência do select equivale ao SQL select * • Para que o inner join do exemplo anterior devolva apenas os itens (e não 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 têm Bids (inner join)

  48. Joins implícitos • Ocorrem em associações *-para-um (caminho convergente) e nunca em caminhos *-para-muitos • O HQL a seguir causa três 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 itemjoinitem.categoryas cat join item.successfulBid as winningBid where cat.name like 'Laptop%‘ and winningBid.amount > 100 Joins implícitossendo revelados

  49. Produtos cartesianos (theta style joins) • Permite recuperar todas as combinações possíveis de instâncias de duas ou mais classes • Útil em classes não associadas • Condição de join deve estar na cláusula where • Apenas inner-join pode ser usado neste caso • Exemplo • Pode-se usar uma cláusula select para filtrar os resultados Produto cartesiano 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(); User user = (User) pair[0]; LogRecord log = (LogRecord) pair[1]; } Condição de join

  50. Comparação de identificadores • Queries que comparam chaves-primárias são realizados implicitamente em HQL • É o mesmo que comparar instâncias • Os dois queries .... • from Item i, User u where i.seller = u and u.username = 'steve' • from Item i, Bid b where i.seller = b.bidder • São respectivamente equivalentes aos queries: • 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 parâmetros (tipo, o id) para comparação.

More Related