JPA Consultas e Dicas

Olá, tudo bem?

Existem diversos tutoriais na internet e, que inclusive aqui no blog, mostram como realizar diversas tarefas com JPA.

Vejo diversas pessoas em fóruns com dúvidas sobre Queries em JPA e são necessários diversos links para mostrar uma solução sobre o assunto.

Até hoje não encontrei nenhum post que unisse um bom material sobre queries com JPA, dicas de performance/utilização, código fonte para download…
Challenge Accepted

Hoje nós veremos aqui:

  • Página 2: Classes de modelo e classe para gerar os dados do banco de dados
  • Página 3: Utilizando método find; utilizar getReference para melhor performance, exibindo parâmetros da query no console através do lo4j
  • Página 4: JPQL: Consultas com parâmetros simples ou objetos, Joins, Order By, Navegando pelos relacionamentos
  • Página 5: JPQL: Funções: AVG, COUNT, MAX, MIN, TRIM, SUM, UPPER, LOWER, MOD, LENGHT, SQRT; Utilizar HAVING, GROUP BY
  • Página 6: JPQL: Condições: LIKE, IN, DISTINCT, EMPTY, BETWEEN, NULL, MEMBER OF, EXISTS (Subqueries), ANY, ALL, SOME, CONCAT, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCATE, SIZE, SUBSTRING
  • Página 7: JPA: NamedQuery, pesquisando com data, cuidados com getSingleResult
  • Página 8: JPA: NativeQuery, NamedNativeQuery
  • Página 9: JPA: Mapeamentos Complexos com NativeQuery
  • Página 10: JPA: Otimizar Queries com EJB
  • Página 11: JPA: Paginação
  • Página 12: JPA: Hints para Banco de Dados
  • Página 13: JPA: Criando um objeto dentro da consulta
  • Página 14: JPQL: Bulk Update e Delete
  • Página 15: JPA: Criteria

Você verá durante o decorrer desse post que o seguinte código será chamado “CodeGenerator.generateData()”. Essa classe tem apenas a função de gerar dados para as pesquisas demonstradas aqui tenham dados.

Na última página do post você irá encontrar o código fonte do projeto para download.

Nesse post iremos utilizar JPA 2.0 com Hibernate de provider. O banco de dados será o HSQLDB e já irá em anexo ao código do projeto. Você poderá baixar e executar o projeto sem a necessidade de configurações extras. Não irei mostrar como montar o ambiente com o HSQLD, pois o foco do post de hoje é apenas ver o funcionamento do JPA para consultas.

Esse post não visa demonstrar boas práticas para camadas de persistência, apenas demonstrar como são realizadas consultas e operações com o banco de dados.

70 thoughts on “JPA Consultas e Dicas

    • Jaisson, boa noite.

      Obrigado pela força.

      Eu já estava querendo escrever sobre esse assunto, mas foi sua pergunta o gatilho para esse post…

      O ~* eu utilizei até como exemplo. =D

  1. sabe como se faz consulta com o relacionamento NxN
    é a mesma coisa que se faz como o 1xN como você mostrou:
    select p from Person p left join fetch p.dogs

  2. Como faço para paginar sem que o jpa fique realizando diversas consultas no banco, pois tenho uma tabela com milhões de registros e não posso trazer toda para um List e nem ficar realizando select a cada paginada, preciso abrir um cursor igual o JDBC faz. Vlw

    • Olá Mauricio, bom dia.

      Nunca ouvi falar dessa opção de cursor no JPA.

      O que você poderia fazer era trazer toda a informação para a memória e pagina “na unha”. Uma alternativa seria ter uma classe que fosse um Singleton e que tivesse essas informações na memória compartilhada para todos os usuários. Apenas tome cuidado para não estourar a memória.

      Obrigado pela visita.

  3. Olá, consegui resolver a questão do cursor. O JPA em si não possui essa funcionalidade, porém o Hibernate tem e é muito fácil de usar. Chama-se ScrollableResults, caso um dia precise pode entrar em contato mauricioadl@ig.com.br.
    Mais uma vez parabéns pelo blog e um forte abraço.

    • Maurício, bom dia.

      Muito obrigado pelo feedback e pela dica dessa opção do Hibernate.

      Vai ajudar outros que aqui tiverem a mesma dúvida. [=

  4. Parabéns pelo post. Estou precisando de uma ajuda. Preciso fazer um filtro onde os registos estão gravados com data e hora em um atributo do tipo Calendar, preciso passar uma data e trazer tudo que tem com aquela data independente da hora. Ex: DataGravada – 01/08 17:00, 01:08 18:00, 03/08 13:00; preciso filtrar os registros só do dia 01/08, no caso viria os dois.

    • Olá Leonardo, boa tarde.

      O modo de fazer a consulta já se encontra no post.

      Se o que você precisa é de ajuda para realizar a lógica você pode postar o seu código atual em http://www.guj.com.br

      O pessoal é gente boa e oferece esse tipo de consultoria gratuita.

      Obrigado pela visita

  5. Opa, finalmente um tutorial mais realista… Pra mim fazer uma coisa simples no Java to com 4 livros aqui foi semanas pra fazer a droga de um webservice… E ninguém tem ideia do que fazer e como fazer.

    Frequento um monte de fórum e lista e todo mundo vira e volta fica “perdidaço” com essas coisas.

    Apesar de ser bem amador, as poucas coisas que to fazendo do publicando por ai, espero que ajude alguém que ta iniciando em java. Recursos tem mas é esquisito.

    Eu acabei fazendo a consulta atravez do glassfish usando connect, ResultSet e criando classes que mexem direto com esses dados (usando sql na unha xD).

    Estou lendo esse artigo, pois esse lance de persistencia/hibernate etc… parece trazer muito mais praticidade na hora de implementar as regras de negocio em si (eu acho). KKK

    Valeu o artigo camarada

      • Camarada veja se tu pode me dar um palpite. Eu conclui a parte principal do meu primeiro webservice aqui, usando glassfish. A aplicação cliente se conecta e baixa um XML contendo todos os dados que ela precisa para alimentar um DB local. Esta funcionando show de bola. Só que eu usei consulta direta com Initial Context, Ai pesquisando com um resultset com uma classe pra cada tabela. Ai quando eu quero algo especifico eu passo uma string com um “AND X = Y”. Bom bla bla, acho que deu pra entender o que fiz :3 rs. A duvida é, assim funciona mas em relação ao JPA/Persistence tem problema de performance ou algum ponto negativo no que fiz?

        • Anderson, boa tarde.

          Não consegui entender sua dúvida.

          Se eu entendi correto, a todo momento você cria uma String de consulta contendo os parâmetros. É isso?

          Se for, essa prática não é aconselhada. Pode gerar problemas de perfomance e problemas de SQL Injection.

          O melhor e deixar sua query já salva com parâmetros e depois popular.

          Espero ter ajudado, até a próxima.

        • Opa cara, valeu a resposta e me desculpe a “noobice” =/
          Mas recorro a quem se dispõe a ajudar em troca ajudo quem precisa também :3

          Tu comentou que não entendeu bem o que eu disse, pois bem veja esta classe:

          public class QueryClassetabela {
          
              private String filtros;
              private Connection con;
              private Statement stmt;
              private ResultSet rs;
              private boolean ApagaCon;
          
              public QueryClassetabela(String filtros, Connection con) {
                  this.filtros = " " + filtros;
                  if (con != null) {
                      this.con = con;
                      ApagaCon = false;
                  } else {
                      //classe que crio a conexão
                      this.con = ConnectionFactory.getConnection();                
                      ApagaCon = true;
                  }
                  this.stmt = this.con.createStatement();
              }
          
              public List<ClasseTabela> ListaTabela() {
                  List<ClasseTabela> lista = new ArrayList<ClasseTabela>();
                  String sql = "SELECT colunaA, ColunaB FROM Tabela n"
                          + filtros;
                  try {
                      rs = stmt.executeQuery(sql);
                      while (rs.next()) {
                          ClasseTabela tabela = new ClasseTabela();
                          tabela.setColunaA(rs.getInt("colunaA"));
                          tabela.setColunaB(rs.getInt("colunaB"));
                          lista.add(tabela);
                      }
                  } catch (SQLException ex) {
                      //
                  } finally {
                      if (ApagaCon) {
                          ConnectionFactory.closeConnection(con, stmt, rs);
                      }
                  }
                  return lista;
              }
          }
          

          Basicamente eu tenho uma desta pra cada tabela que eu uso nas minhas consultas do sistema que estou fazendo, só que eu ainda tenho duvida se isto é um procedimento recomendado ou eu to fazendo porcaria? To lendo vários livros e tópicos aqui mas a duvida permanece. =/

          • Olá Anderson, boa tarde.

            Então eu realmente consegui entender.

            Então minha resposta permanece a mesma. [=

            1) Recriar a String de query toda hora detona a performance caso muito utilizado.
            2) Concatenar String de query pode levar ao ataque SQL Injection.

            Um bom livro que você pode ler é: Pro JPA 2: Mastering the Java™ Persistence API – Mike Keith, Merrick Schincariol

            Obrigado pela visita.

  6. É possível no JPA bloquear uma tabela enquanto uma transação esta ativa e so desbloquear para a proxima transação quando a que estava ativa não estiver mais?

    Obrigado

    • Fernando, boa tarde.

      É possível realizar o LOCK de vários modos.

      Você pode encontrar o conhecimento necessário no livro PRO JPA 2 que está listado na sessão de livros aqui no blog.

      É necessário ter bastante conhecimento no que está sendo feito, pois isso irá afetar toda a aplicação.

      Obrigado pela visita.

  7. Parabéns pelo post, blog.. acompanho direto, e já me ajudou em muitas coisas!! Parabéns mesmo!

    Gostaria de tirar uma dúvida, se possível..
    Se eu quiser fazer uma consulta, listando todos os dogs que uma determinada Person tem.. como poderia fazer?

    public List procuraPerson(EntityManager em, Person person){
    List lista = null;
    Query query = em.createQuery(“select d from Dog d where d.person = :person”);
    query.setParameter(“person”, person);
    lista = query.getResultList();
    return lista;
    }

    Algo assim?? Estou tentando fazer algo parecido a alguns dias, mas sem sucesso =/
    Obrigado!!
    Abraços

  8. Olá ! Vc sempre faz o controle de transação “na mão” ? Pq não usa algo como o @Transactional do spring ? Show o seu blog !!

    • Olá Yesslei, boa noite.

      Na verdade eu utilizei transação “na mão” nesse post pois os exemplos são simples. Não está rodando em nenhum container e eu não queria adicionar o Spring, pois o foco do post é o JPA.

      Mas eu prefiro 100% em deixar alguém cuidado da transação por mim, como o Spring ou EJB. Acho mais simples e prático. [=

      Obrigado pela visita e pelo apoio.

      • Entendi Hebert.
        É q vi varios posts seu em q abre, commita e fecha as transações na unha. Dai pensei q sempre fazia assim e qria saber se teria um ganho.
        Se me permitir sair um pouco do foco desse post, conhece alguma anotação q diga pro hibernate não criar uma tabela no banco que represente uma entidade ?
        Eu tenho algumas entidades e estou usando o create-drop por enquanto. Mas existe uma tabela/entidade q não qro q ele crie nada e nem apague nada dela.

        Sempre q tenho duvidas com jpa e hibernate, venho primeiro aki rsrsrsrs

        • Yesslei, boa tarde.

          Não sei se eu entendi sua pergunta direito.

          Se você não quer que uma classe seja criada como tabela, basta não anotar com o Entity.

          Caso você não queria que um atributo seja criado, anote esse atributo com @Transient.

          Obrigado pela visita. [=

  9. Olá amigo !
    Poderia me ajudar em uma questão ?

    Tenho a entidade Cliente que possue uma lista de Contratos.
    Acontece que quando busco um cliente pelo nome, o retorno é uma lista com o mesmo cliente mais de uma vez.
    Reparei número de vezes que o cliente aparece é o mesmo número de contratos que esse cliente tem.
    Meu modelo está assim:

    Cliente:

    @OneToMany(mappedBy = “cliente”, fetch = FetchType.EAGER)
    private List contratos;

    Contrato:

    @ManyToOne
    private Cliente cliente;

    Busca:

    Session hibernateSession = (Session) entityManager.getDelegate();
    Criteria criteria = hibernateSession.createCriteria(Cliente.class).add(Restrictions.eq(“nome”, nomeCliente));
    return criteria.list();

    Sabe o pq q acontece isso ?

    Muito Obrigado.

    • Yesslei, boa tarde.

      Não entendo muito da critéria do Hibernate.

      Você pode fazer duas tentativas. Uma seria chamar o distinct e a outra utilizar um Set ao invés de List.

      Até mais! o_

    • Quando busco assim:
      entityManager.createQuery(“select c from Cliente c where c.nome = :cliente”);

      Ele trás corretamente um único cliente.

  10. Olá amigo,

    Sabe me dizer se existe algum forma de realizar uma consulta via HQL para retornar um objeto pai com apenas alguns objetos filhos selecionados?
    Consegui fazer uma HQL trazendo apenas os objetos pais que contenham filhos com características específicas, mas no retorno da consulta o objeto pai tem todos os objetos filhos e gostaria que fossem retornados apenas os filhos que atendessem os filtros.

    Desde já muito obrigado.

    • Olá Rodrigo, boa noite.

      Se você marcar os objetos “indesejáveis” como lazy, eles vem assim mesmo?

      Obrigado pela visita.

        • Rodrigo, boa noite.

          Desculpe a demora em responder.

          Veja se a implementação de JPA que você está utilizando não está configurada para carregar automaticamente a lista.

          Infelizmente ela tem autonomia para fazer isso. Veja também se todas as listas que não devem vir estão marcadas como Lazy.

          Obrigado pela visita.

          • Rodrigo, bom dia.

            Realmente não estou conseguindo entender seu problema.

            Precisaria analisar mais código e aqui o código não fica bem de analisar.

            Faça o seguinte, abra um post no GUJ que ajudaremos por lá.

  11. Olá uaihebert, comecei a estudar a criação de querys agora e estou com algumas dúvidas, você poderia me ajudar? Se a resposta for sim as dúvidas são: Como faço uma junção entre duas entidades em uma query? Uma query pode ficar sem parâmetros?

    • Alisson, boa tarde.

      1) A junção é feita através de Join e Left Join, ambos explicados neste post. [=

      2) Você tentou? Qual foi o resultado? [=

      Obrigado pela visita.

  12. Muito bom seu tutorial e suas explicações foram realmente muito didáticas! Parabéns! Já tinha trabalhado com JPA, mas nunca tinha visto tudo de uma forma tão intensa e explicativa…

  13. Olá.
    Primeiramente parabéns pelo blog, muito bom mesmo!

    Estou com uma duvida que deve ser relativamente simples para você:
    Estou com problemas por causa do case sensetive, como faço para ele desconsiderar isso ao fazer uma busca com like?

  14. Herbert boa tarde, primeiramente parabéns pelo post, realmente é muito bom, muito bem explicado e didático… melhor até agora que vi na net, até adicionei aos favoritos para consultar de vez em quando…
    Agora um questionamento, estou verificando que estranhamente quando mando buscar uma coleção através do” join fetch” alguns registros estão vindo duplicados dentro da coleção… por exemplo tenho o objeto aluno que tem fez um monte de provas, tem o objeto provafeita que é composta pelo aluno, quando mando buscar com esta query:” Select r FROM Prova r join fetch r.questoes WHERE r.alunos= :user” algumas provas vêm duplicadas nesta coleção… vc já passou por isso?

    • Rodrigo, boa tarde.

      Existem duas abordagem para resolver esse ‘problema’ (que na verdade é um comportamento normal de SQL).

      1)Você pode usar distinct na query
      2)Você pode usar Set juntamente com hashCode/equals em suas entities.

      Desculpe a demora em responder. Estou muito ocupado nesses últimos dias.

  15. Boa tarde,

    Em primeiro lugar lhe parabenizo pelo post, muito bom mesmo!!!

    Tenho uma dúvida.

    Tenho uma classe que possui três listas, usando o join fecth eu só posso trazer uma ao buscar essa classe. Como poderia resolver isso com JPA?

    Abraços

    • Rafael, boa noite.

      Nesse caso você teria que deixar as listas anotadas com LAZY e criar consultas onde você faz JOIN FETCH na lista desejada.

      Obrigado pela visita.

  16. Olá Hebert, bom dia. Primeiramente gostaria de lhe parabenizar pela excelente inciativa e disposição em ajudar. Estou com um baita problema quando tento executar uma operação em lote. O local do meu sistema em que o problema reside é quando vou subtrair os produtos comprados da tabela estoque. Todos os métodos que eu implementei funcionaram direitinho do sistema para o banco, mas nenhum método tentado para atualizar a visão funciona. Pesquisei e tentei tudo, inclusive iniciar uma nova transação (criei um novo EntityManager na transação), limpar o cache (com o this.em.getEntityManagerFactory().getCache().evictAll();), limpar o EntityManager com o em.clear(). Tentei usar o merge(), o persist(), o refresh(), o find() e até o query.executeUpdate(), com JPQL. Nada funcionou até agora, o problema do BULK / BATCH UPDATE continua, persiste no banco, mas não atualiza a visão. Não sei se influencia, mas em momento algum precisei buscar a lista de estoques, através de chamadas proprias a entidade estoque. Todos os dados relativos a estoque que utilizei vem de uma NativeQuery. Isso influencia? Quando eu uso estes mesmos métodos para alterar o estoque, através de chamadas usando Ajax, funciona perfeitamente, a diferença é que elas não são de Batch. Desculpe-me caso não tenha sido 100% preciso ou prolixo, mas já tentei de tudo. Desde já lhe agradeço.

    • Erico, bom dia.

      Problemas desse tipo geralmente estão relacionados à arquitetura do projeto e é necessário uma boa análise.

      O que eu diria é que, em sua visão faça duas chamadas:
      1) Controller (chama o serviço) —> Serviço abre a transação e executa a processamento em lote. —> Serviço faz commit/flush e fecha a transação
      2) Controller (chama o serviço) —> realizar a consulta em uma transação nova

      Se você está utilizando OpenSessionInView essa estratégia não será possível. =/ Nesse caso você terá que suspender a transação atual e criar uma nova, e sem um framework (como Spring ou EJB) isso vai dar muito trabalho.

      Obrigado pela visita.

  17. Bom dia Hebert e parabéns pelo seu ótimo trabalho!
    Apanhando um pouco na migração SQL Server para HQL resolvi baixar o seu fonte e testá-lo. Mas, ao executar a Page05.java ocorreu o mesmo erro que estou tendo aqui: org.hibernate.exception.SQLGrammarException: could not execute query

    Segue o debug da Page05.java:
    37.0
    10:25:56,846 ERROR JDBCExceptionReporter:234 – A coluna ‘Person.address_id’ é inválida na lista de seleção porque não está contida em uma função de agregação nem na cláusula GROUP BY.
    Exception in thread “main” javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute query

    A única solução seria incluir nas cláusulas Select e Group by cada propriedade da classe Person deixando de obter o bean automático?

    Obrigado, Rogerio.

    • Rogério, boa tarde.

      Se eu entendi direito seu problema, você deve incluir todos os campos que estão sendo selecionados em sua query.

      Não existe um modo automático para fazer isso.

      Obrigado pela visita.

Leave a Comment