Consultas de criterios JPA

1. Información general

En este tutorial, analizaremos una función JPA muy útil: consultas de criterios.

No solo nos permite escribir consultas sin hacer SQL en bruto, sino que también nos da cierto control orientado a objetos sobre las consultas, que es una de las características principales de Hibernate. La API de criterios nos permite crear un objeto de consulta de criterios mediante programación, donde podemos aplicar diferentes tipos de reglas de filtrado y condiciones lógicas.

Desde Hibernate 5.2, la API de criterios de Hibernate está en desuso y el nuevo desarrollo se centra en la API de criterios de JPA. Exploraremos cómo utilizar Hibernate y JPA para crear consultas de criterios.

2. Dependencias de Maven

Para ilustrar la API, usaremos la implementación de JPA de referencia: Hibernate.

Para usar Hibernate, asegúrese de agregar la última versión a su archivo pom.xml :

 org.hibernate hibernate-core 5.3.2.Final 

La última versión de Hibernate se puede encontrar aquí.

3. Ejemplo simple usando criterios

Comencemos por ver cómo recuperar datos mediante consultas de criterios. Veremos cómo obtener todas las instancias de una clase en particular de la base de datos.

Tenemos una clase Item que representa la tupla "ITEM" en la base de datos:

public class Item implements Serializable { private Integer itemId; private String itemName; private String itemDescription; private Integer itemPrice; // standard setters and getters }

Veamos una consulta de criterios simple que recuperará todas las filas de " ITEM" de la base de datos:

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();

La consulta anterior es una demostración simple de cómo obtener todos los elementos. Veamos qué se hizo, paso a paso:

  1. Cree una instancia de Session desde el objeto SessionFactory
  2. Cree una instancia de C riteriaBuilder llamando al método getCriteriaBuilder ()
  3. Cree una instancia de CriteriaQuery llamando al método CriteriaBuilder createQuery ()
  4. Crear una instancia de consulta llamando a la Sesión CreateQuery () método
  5. Llame al método getResultList () del objeto de consulta que nos da los resultados

Ahora que hemos cubierto los conceptos básicos, pasemos a algunas de las características de la consulta de criterios:

3.1. Usando Expresiones

El CriteriaBuilder se puede utilizar para restringir los resultados de la consulta en base a las condiciones específicas. Usando CriteriaQuery método where () y proporcionando Expresiones creadas por CriteriaBuilder.

A continuación, se muestran algunos ejemplos de expresiones de uso común :

Para obtener artículos que tengan un precio superior a 1000:

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

A continuación, obtener artículos con itemPrice menor que 1000:

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

Los elementos que tienen itemNames contienen Chair :

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

Registros que tienen itemPrice entre 100 y 200:

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

Para verificar si la propiedad dada es nula:

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

Para verificar si la propiedad dada no es nula:

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

También puede usar los métodos isEmpty () e isNotEmpty () para probar si una lista dentro de una clase está vacía o no.

Ahora surge inevitablemente la pregunta de si podemos combinar dos o más de las comparaciones anteriores o no. La respuesta es, por supuesto, sí: la API Criteria nos permite encadenar expresiones fácilmente :

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 agregar dos expresiones con operaciones lógicas:

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

Elementos con las condiciones definidas anteriormente junto con el OR lógico :

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

Para obtener elementos que coincidan con las condiciones definidas anteriormente junto con un AND lógico :

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

3.2. Clasificación

Ahora que conocemos el uso básico de Criteria , echemos un vistazo a las funcionalidades de clasificación de Criteria .

En el siguiente ejemplo ordenamos la lista en orden ascendente del nombre y luego en orden descendente del precio:

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

En la siguiente sección, veremos cómo hacer funciones agregadas.

3.3. Proyecciones, agregados y funciones de agrupación

Hasta ahora hemos cubierto la mayoría de los temas básicos. Ahora echemos un vistazo a las diferentes funciones agregadas:

Obtener el recuento de filas:

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();

El siguiente es un ejemplo de funciones agregadas:

Función agregada para promedio :

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();

Otros métodos agregados útiles que están disponibles son sum () , max () , min () , count (), etc.

3.4. CriteriaUpdate

A partir de JPA 2.1, existe soporte para realizar actualizaciones de la base de datos utilizando la API Criteria .

CriteriaUpdate has a set() method that can used to provide new values for database records:

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();

In the above snippet, we create an instance of CriteriaUpdate from the CriteriaBuilder and use its set() method to provide new values for the itemPrice. To update multiple properties, we just need to call the set() method multiple times.

3.5. CriteriaDelete

CriteriaDelete, as its name implies, enables a delete operation using the Criteria API. All we need is to create an instance of CriteriaDelete and use the where() method to apply restrictions:

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. Advantage Over HQL

In the previous sections we've covered how to use Criteria Queries.

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

We can simply write more flexible, dynamic queries compared to plain HQL. The logic can be refactored with the IDE and has all the type-safety benefits of the Java language itself.

There are of course some disadvantages as well, especially around more complex joins.

So, generally speaking, we'll have to use the best tool for the job – that can be the Criteria API in most cases, but there are definitely cases where we'll have to go lower level.

5. Conclusion

In this article, we focused on the basics of Criteria Queries in Hibernate and JPA, and also on some of the advanced features of the API.

El código discutido aquí está disponible en el repositorio de Github.