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:
-
Crie uma instância deSession a partir do objetoSessionFactory
-
Crie uma instância de CriteriaBuilder chamando o métodogetCriteriaBuilder()
-
Crie uma instância deCriteriaQuery chamandoCriteriaBuildercreateQuery() method
-
Crie uma instância deQuery chamando o métodoSessioncreateQuery()
-
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.