Consultas de Critérios JPA

Consultas de Critérios JPA

1. Visão geral

Neste tutorial, discutiremos um recurso JPA muito útil - Consultas de critérios.

Isso não apenas nos permite escrever consultas sem fazer SQL bruto, mas também nos fornece algum controle orientado a objetos sobre as consultas, que é um dos principais recursos do Hibernate. A API de critérios permite criar programaticamente um objeto de consulta de critérios, onde podemos aplicar diferentes tipos de regras de filtragem e condições lógicas.

Since Hibernate 5.2, the Hibernate Criteria API is deprecated and new development is focused on the JPA Criteria API. Exploraremos como usar Hibernate e JPA para construir consultas de critérios.

2. Dependências do Maven

Para ilustrar a API, usaremos a implementação JPA de referência - Hibernate.

Para usar o Hibernate, certifique-se de adicionar a versão mais recente dele ao arquivopom.xml:


    org.hibernate
    hibernate-core
    5.3.2.Final

A última versão do Hibernate pode ser encontradahere.

3. Exemplo simples usando critérios

Vamos começar examinando como recuperar dados usando consultas de critérios. Veremos como obter todas as instâncias de uma classe específica do banco de dados.

Temos uma classeItem que representa a tupla“ITEM” no banco de dados:

public class Item implements Serializable {

    private Integer itemId;
    private String itemName;
    private String itemDescription;
    private Integer itemPrice;

   // standard setters and getters
}

Vejamos uma consulta de critérios simples que recuperará todas as linhas de “ITEM” do banco de dados:

Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery cr = cb.createQuery(Item.class);
Root root = cr.from(Item.class);
cr.select(root);

Query query = session.createQuery(cr);
List results = query.getResultList();

A consulta acima é uma demonstração simples de como obter todos os itens. Vamos ver o que foi feito, passo a passo:

  1. Crie uma instância deSession a partir do objetoSessionFactory

  2. Crie uma instância de CriteriaBuilder chamando o métodogetCriteriaBuilder()

  3. Crie uma instância deCriteriaQuery chamandoCriteriaBuildercreateQuery() method

  4. Crie uma instância deQuery chamando o métodoSessioncreateQuery()

  5. Chame o métodogetResultList() do objetoquery que nos dá os resultados

Agora que cobrimos o básico, vamos passar para alguns dos recursos da consulta de critérios:

3.1. UsandoExpressions

The CriteriaBuilder can be used to restrict query results based on specific conditions. Usando o métodoCriteriaQuery where() e forneçaExpressions criado porCriteriaBuilder.

Aqui estão alguns exemplos deExpressions comumente usados:

Para obter itens com preço superior a 1000:

cr.select(root).where(cb.gt(root.get("itemPrice"), 1000));

Em seguida, obtendo itens comitemPrice menor que 1000:

cr.select(root).where(cb.lt(root.get("itemPrice"), 1000));

Itens comitemNames contêmChair:

cr.select(root).where(cb.like(root.get("itemName"), "%chair%"));

Registros comitemPrice entre 100 e 200:

cr.select(root).where(cb.between(root.get("itemPrice"), 100, 200));

Para verificar se a propriedade especificada é nula:

cr.select(root).where(cb.isNull(root.get("itemDescription")));

Para verificar se a propriedade especificada não é nula:

cr.select(root).where(cb.isNotNull(root.get("itemDescription")));

Você também pode usar os métodosisEmpty()eisNotEmpty() para testar se umList dentro de uma classe está vazio ou não.

Agora, inevitavelmente, surge a pergunta, se podemos combinar duas ou mais das comparações acima ou não. A resposta é, claro, sim -the Criteria API allows us to easily chain expressions:

Predicate[] predicates = new Predicate[2];
predicates[0] = cb.isNull(root.get("itemDescription"));
predicates[1] = cb.like(root.get("itemName"), "chair%");
cr.select(root).where(predicates);

Para adicionar duas expressões com operações lógicas:

Predicate greaterThanPrice = cb.gt(root.get("itemPrice"), 1000);
Predicate chairItems = cb.like(root.get("itemName"), "Chair%");

Itens com as condições definidas acima unidas comLogical OR:

cr.select(root).where(cb.or(greaterThanPrice, chairItems));

Para obter itens que correspondam às condições definidas acima unidos comLogical AND:

cr.select(root).where(cb.and(greaterThanPrice, chairItems));

3.2. Ordenação

Agora que sabemos o uso básico deCriteria, vamos dar uma olhada nas funcionalidades de classificação deCriteria.

No exemplo a seguir, ordenamos a lista em uma ordem crescente do nome e, em seguida, em uma ordem decrescente do preço:

cr.orderBy(
  cb.asc(root.get("itemName")),
  cb.desc(root.get("itemPrice")));

Na próxima seção, veremos como fazer funções agregadas.

3.3. Projeções, agregados e funções de agrupamento

Até agora, cobrimos a maioria dos tópicos básicos. Agora vamos dar uma olhada nas diferentes funções de agregação:

Obter contagem de linhas:

CriteriaQuery cr = cb.createQuery(Long.class);
Root root = cr.from(Item.class);
cr.select(cb.count(root));
Query query = session.createQuery(cr);
List itemProjected = query.getResultList();

A seguir, é apresentado um exemplo de funções agregadas:

FunçãoAggregate paraAverage:

CriteriaQuery cr = cb.createQuery(Double.class);
Root root = cr.from(Item.class);
cr.select(cb.avg(root.get("itemPrice")));
Query query = session.createQuery(cr);
List avgItemPriceList = query.getResultList();

Outros métodos agregados úteis que estão disponíveis sãosum(),max(),min(),count() etc.

3.4. CriteriaUpdate

A partir do JPA 2.1, há suporte para realizar atualizações de banco de dados usando a APICriteria.

CriteriaUpdate tem um métodoset() que pode ser usado para fornecer novos valores para os registros do banco de dados:

CriteriaUpdate criteriaUpdate = cb.createCriteriaUpdate(Item.class);
Root root = criteriaUpdate.from(Item.class);
criteriaUpdate.set("itemPrice", newPrice);
criteriaUpdate.where(cb.equal(root.get("itemPrice"), oldPrice));

Transaction transaction = session.beginTransaction();
session.createQuery(criteriaUpdate).executeUpdate();
transaction.commit();

No snippet acima, criamos uma instância deCriteriaUpdate<Item> deCriteriaBuildere usamos seu métodoset() para fornecer novos valores paraitemPrice.. Para atualizar várias propriedades, só precisamos chame o métodoset() várias vezes.

3.5. CriteriaDelete

CriteriaDelete, como o próprio nome indica, permite uma operação de exclusão usando a APICriteria. Tudo o que precisamos é criar uma instância deCriteriaDeletee usar o métodowhere() para aplicar restrições:

CriteriaDelete criteriaDelete = cb.createCriteriaDelete(Item.class);
Root root = criteriaDelete.from(Item.class);
criteriaDelete.where(cb.greaterThan(root.get("itemPrice"), targetPrice));

Transaction transaction = session.beginTransaction();
session.createQuery(criteriaDelete).executeUpdate();
transaction.commit();

4. Vantagem sobre HQL

Nas seções anteriores, cobrimos como usar as consultas de critérios.

Claramente,the main and most hard-hitting advantage of Criteria queries over HQL is the nice, clean, Object Oriented API.

Podemos simplesmente escrever consultas dinâmicas e mais flexíveis em comparação com o HQL simples. A lógica pode ser refatorada com o IDE e possui todos os benefícios de segurança de tipo da própria linguagem Java.

Obviamente, também existem algumas desvantagens, especialmente em torno de junções mais complexas.

Então, de modo geral, teremos que usar a melhor ferramenta para o trabalho - que pode ser a API de critérios na maioria dos casos, mas definitivamente há casos em que teremos que ir para um nível inferior.

5. Conclusão

Neste artigo, nos concentramos nos conceitos básicos de consultas de critério no Hibernate e JPA, e também em alguns dos recursos avançados da API.

O código discutido aqui está disponível emGithub repository.