Hibernate

Hibernate

Tutorial Hibernate: Consultas JPQL avanzadas

Hibernate consultas JPQL avanzadas: guía. Aprende a realizar consultas JPQL avanzadas en Hibernate mediante ejemplos prácticos y detallados.

Introducción

Java Persistence Query Language (JPQL) extiende las capacidades más allá de las consultas básicas, permitiendo operaciones complejas que incluyen el uso de JOIN, GROUP BY, HAVING y subconsultas. 

Estas características avanzadas habilitan la manipulación y el análisis profundo de datos relacionados entre diferentes entidades, agrupación de resultados, y la aplicación de condiciones a grupos de resultados, además de permitir consultas anidadas dentro de otras consultas.

Uso de JOIN para unir entidades

JPQL ofrece la capacidad de unir varias entidades mediante la cláusula JOIN, lo que permite realizar consultas sobre relaciones entre entidades. Los tipos de JOIN incluyen INNER JOIN, LEFT JOIN, RIGHT JOIN, y FULL JOIN, cada uno adecuado para distintos escenarios de consulta.

Consideremos un caso en el que necesitamos seleccionar todos los empleados y los departamentos a los que pertenecen. Suponiendo que cada entidad Empleado tiene una relación con una entidad Departamento, la consulta podría ser:

// Crear una instancia de Query para realizar un JOIN
jakarta.persistence.Query query = entityManager.createQuery("SELECT e, d FROM Empleado e JOIN e.departamento d");

// Ejecutar la consulta y recuperar la lista de resultados
List<Object[]> resultado = query.getResultList();

La consulta "SELECT e, d FROM Empleado e JOIN e.departamento d" indica que deseamos seleccionar entidades Empleado y sus respectivos Departamentos:

  • SELECT e, d: Esta parte de la consulta indica qué queremos obtener como resultado. Aquí, estamos diciendo que queremos seleccionar entidades completas, no solo campos individuales. e representa a un objeto de la clase Empleado, y d representa a un objeto de la clase Departamento. Al usar SELECT e, d, estamos pidiendo que la consulta nos devuelva una lista de arrays, donde cada array contiene dos objetos: uno de la clase Empleado y otro de la clase Departamento.
  • FROM Empleado e: Aquí, especificamos de dónde queremos seleccionar los objetos Empleado. Usamos e como un alias para referenciar cualquier instancia de Empleado dentro de esta consulta. Es como decir: "Considera cada fila de la tabla Empleado (o cada instancia de la entidad Empleado) y refiérete a ella como e".
  • JOIN e.departamento d: Aquí se establece la relación entre las dos entidades que queremos unir en nuestra consulta. JOIN es una operación que nos permite combinar filas de dos o más tablas (o entidades, en el caso de JPQL) basándose en una relación común entre ellas. En este caso, e.departamento hace referencia a la propiedad departamento dentro de la entidad Empleado. La parte d al final es un alias que usamos para referirnos a las instancias de Departamento que están relacionadas con Empleado a través de esta propiedad. Por lo tanto, la consulta une cada empleado (e) con su respectivo departamento (d) basándose en la relación definida en el modelo de datos de la aplicación.

Es decir, esta consulta JPQL nos permite traer juntos, en un solo resultado, a los empleados y sus respectivos departamentos, haciendo uso de la relación que existe entre estas dos entidades en el modelo de datos.

Agrupación de resultados con GROUP BY

La cláusula GROUP BY se utiliza para agrupar los resultados de una consulta por uno o más atributos. Esto es especialmente útil cuando se combinan con funciones de agregación como COUNT, MAX, MIN, SUM, y AVG.

Imaginemos que queremos contar el número de empleados en cada departamento:

// Preparar la consulta JPQL con agrupación
jakarta.persistence.Query query = entityManager.createQuery("SELECT d.nombre, COUNT(e) FROM Empleado e JOIN e.departamento d GROUP BY d.nombre");

// Ejecutar la consulta y recuperar los resultados
List<Object[]> resultado = query.getResultList();

La consulta "SELECT d.nombre, COUNT(e) FROM Empleado e JOIN e.departamento d GROUP BY d.nombre" es un ejemplo de cómo podemos agrupar datos y aplicar funciones de agregación en un contexto donde las entidades están relacionadas:

  • SELECT d.nombre, COUNT(e): Esta es la parte de la consulta que define qué información queremos obtener como resultado. Aquí, estamos seleccionando dos cosas: el nombre del departamento (d.nombre) y el número total de empleados (COUNT(e)) que pertenecen a cada departamento. COUNT(e) es una función de agregación que cuenta la cantidad de filas que cumplen con ciertos criterios, en este caso, cuántos empleados (e) hay en cada departamento.
  • FROM Empleado e: Indica la entidad desde la cual estamos iniciando nuestra consulta. Usamos e como un alias para referirnos a las instancias de la entidad Empleado. Es decir, estamos diciendo que nuestra consulta va a trabajar sobre la tabla o entidad Empleado y que para esta consulta, nos referiremos a cada fila o instancia de esta entidad como e.
  • JOIN e.departamento d: Como en el ejemplo anterior, se unen estas dos entidades basándose en una relación existente entre ellas. En este caso, estamos diciendo que queremos unir cada empleado (e) con su respectivo departamento.
  • GROUP BY d.nombre: La cláusula GROUP BY se usa para agrupar las filas que tienen el mismo valor en columnas especificadas, lo que permite realizar operaciones de agregación, como contar, sumar, promediar, etc., sobre cada grupo. En este caso, estamos agrupando los resultados por el nombre del departamento (d.nombre). Esto significa que la consulta agrupará a todos los empleados según el departamento al que pertenecen, identificado por el nombre del departamento.

La consulta, por tanto, nos dará como resultado un conjunto de filas, donde cada fila contiene el nombre de un departamento y el número total de empleados que trabajan en ese departamento.

Uso de HAVING para filtrar grupos

La cláusula HAVING se utiliza en combinación con GROUP BY para aplicar condiciones a los grupos de resultados. Es similar a WHERE, pero se aplica después de agrupar los resultados.

Si queremos encontrar departamentos con más de 10 empleados:

// Preparación de la consulta JPQL con HAVING
jakarta.persistence.Query query = entityManager.createQuery("SELECT d.nombre, COUNT(e) FROM Empleado e JOIN e.departamento d GROUP BY d.nombre HAVING COUNT(e) > 10");

// Ejecución de la consulta y recuperación de los resultados
List<Object[]> resultado = query.getResultList();

La consulta "SELECT d.nombre, COUNT(e) FROM Empleado e JOIN e.departamento d GROUP BY d.nombre HAVING COUNT(e) > 10" filtra los grupos de resultados para incluir solo aquellos departamentos donde el conteo de empleados excede 10:

  • SELECT d.nombre, COUNT(e): Al igual que antes, se selecciona el nombre del departamento (d.nombre) y el número total de empleados (COUNT(e)) en cada departamento.
  • FROM Empleado e: Indica que la consulta se basa en la entidad Empleado, utilizando e como alias para referirse a las instancias de esta entidad.
  • JOIN e.departamento d: Esta instrucción une las entidades Empleado y Departamento basándose en la relación definida entre ellas.
  • GROUP BY d.nombre: La consulta agrupa los resultados por el nombre del departamento (d.nombre). Esto significa que todos los empleados se agruparán según el departamento al que pertenecen, y la función de agregación (COUNT(e)) operará sobre estos grupos, contando cuántos empleados hay en cada uno.
  • HAVING COUNT(e) > 10: Aquí es donde esta consulta se distingue de la anterior. La cláusula HAVING funciona de manera similar a WHERE, pero se aplica a grupos de resultados en lugar de a filas individuales. En este caso, HAVING COUNT(e) > 10 filtra los grupos resultantes para incluir solo aquellos que tienen más de 10 empleados. En otras palabras, después de que la consulta haya agrupado a los empleados por departamento y contado cuántos empleados hay en cada grupo, HAVING elimina aquellos grupos (departamentos) que no cumplen con el criterio de tener más de 10 empleados.

El resultado final de esta consulta será un conjunto de filas, donde cada fila contiene el nombre de un departamento y el número total de empleados en ese departamento, pero solo para aquellos departamentos que tienen más de 10 empleados.

Subconsultas

Las subconsultas permiten realizar consultas dentro de otras consultas, proporcionando una poderosa herramienta para operaciones complejas de selección y filtrado.

Supongamos que queremos seleccionar todos los empleados cuyo salario es mayor que el promedio de salarios de la empresa:

// Crear una instancia de Query para una subconsulta
jakarta.persistence.Query query = entityManager.createQuery("SELECT e FROM Empleado e WHERE e.salario > (SELECT AVG(e2.salario) FROM Empleado e2)");

// Ejecutar la consulta y recuperar la lista de resultados
List<Empleado> resultado = query.getResultList();

La consulta "SELECT e FROM Empleado e WHERE e.salario > (SELECT AVG(e2.salario) FROM Empleado e2)" resulta de unir dos consultas. La subconsulta (SELECT AVG(e2.salario) FROM Empleado e2) calcula el salario promedio de todos los empleados. La consulta exterior selecciona aquellos empleados cuyo salario excede este promedio:

  • SELECT e FROM Empleado e: Esta es la consulta principal. Indica que queremos seleccionar entidades completas de tipo Empleado.
  • WHERE e.salario > (...subconsulta...): Esta parte de la consulta utiliza un WHERE para limitar los resultados a aquellos empleados cuyo salario es mayor que el salario promedio de todos los empleados (que se obtiene de una subconsulta).
  • SELECT AVG(e2.salario) FROM Empleado e2: Esta es la subconsulta, que se ejecuta en el contexto de la consulta principal. La subconsulta calcula el salario promedio de todos los empleados. Usamos e2 como un alias para las instancias de Empleado en esta subconsulta para diferenciarlas de las instancias referenciadas en la consulta principal (e). AVG(e2.salario) es una función de agregación que calcula el promedio de los salarios de todos los empleados.
  • La subconsulta se ejecuta primero, calculando el salario promedio de todos los empleados. Luego, este valor promedio se utiliza en la consulta principal como parte de la condición de filtrado e.salario >. Así, solo los empleados cuyo salario es mayor que este promedio son seleccionados y devueltos por la consulta principal.

El resultado de esta consulta será una lista de empleados que ganan más que el promedio de todos los salarios de los empleados, utilizando el mecanismo de subconsulta para calcular dinámicamente el valor de comparación basado en el conjunto de datos completo.

Certifícate en Hibernate con CertiDevs PLUS

Ejercicios de esta lección Consultas JPQL avanzadas

Evalúa tus conocimientos de esta lección Consultas JPQL avanzadas con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Consultas JPQL avanzadas

Hibernate
Código

Configuración con JPA

Hibernate
Test

Tipos de datos personalizados

Hibernate
Puzzle

Consultas Criteria avanzadas

Hibernate
Código

Operaciones en cascada

Hibernate
Test

Anotaciones JPA

Hibernate
Puzzle

Asociación Many To One

Hibernate
Test

Funciones en consultas JPQL

Hibernate
Código

Asociación Many To Many entre Actor y Pelicula

Hibernate
Código

Asociación One To Many entre Curso y Estudiante

Hibernate
Código

Tipos de datos básicos

Hibernate
Test

Consultas Criteria básicas

Hibernate
Código

Asociación Many To Many

Hibernate
Puzzle

CRUD de entidades con asociaciones

Hibernate
Proyecto

Optimización de asociaciones con carga lazy

Hibernate
Código

Asociación One To Many

Hibernate
Puzzle

Configuración con Maven

Hibernate
Test

Asociación One To One

Hibernate
Test

CRUD en Hibernate

Hibernate
Código

Operaciones en cascada

Hibernate
Puzzle

Introducción a Hibernate

Hibernate
Test

Atributos de tipo enum en entidades JPA

Hibernate
Código

Carga de asociaciones en consultas con EntityGraph

Hibernate
Código

Configuración con Gradle

Hibernate
Test

Asociación One To One entre Libro y Resumen

Hibernate
Código

Asociación One To Many

Hibernate
Test

Asociación Many To Many

Hibernate
Test

Creación de entidades

Hibernate
Test

Ciclo de vida de una entidad

Hibernate
Código

Consultas JPQL básicas

Hibernate
Código

Carga de asociaciones en consultas con EntityGraph y anotaciones

Hibernate
Código

Tipos de datos embebidos

Hibernate
Código

Asociación Many To One entre Paciente y Clinica

Hibernate
Código

Asociación Many To One

Hibernate
Puzzle

Optimización de consultas con DTOs

Hibernate
Código

Atributos @Transient en entidades

Hibernate
Código

Asociación One To One

Hibernate
Puzzle

Todas las lecciones de Hibernate

Accede a todas las lecciones de Hibernate y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Hibernate

Hibernate

Introducción Y Entorno

Configuración Hibernate Con Gradle

Hibernate

Introducción Y Entorno

Configuración Hibernate Con Maven

Hibernate

Introducción Y Entorno

Configuración Hibernate Con Jpa

Hibernate

Introducción Y Entorno

Creación De Entidades Jpa

Hibernate

Entidades Jpa Y Tipos De Datos

Tipos De Datos En Hibernate

Hibernate

Entidades Jpa Y Tipos De Datos

Atributos @Transient En Entidades

Hibernate

Entidades Jpa Y Tipos De Datos

Enums En Hibernate

Hibernate

Entidades Jpa Y Tipos De Datos

Tipos De Datos Embebidos

Hibernate

Entidades Jpa Y Tipos De Datos

Crud En Hibernate

Hibernate

Entidades Jpa Y Tipos De Datos

Ciclo De Vida De Una Entidad

Hibernate

Entidades Jpa Y Tipos De Datos

Asociación One To One

Hibernate

Asociaciones Entre Entidades

Asociación One To Many

Hibernate

Asociaciones Entre Entidades

Asociación Many To One

Hibernate

Asociaciones Entre Entidades

Asociación Many To Many

Hibernate

Asociaciones Entre Entidades

Operaciones En Cascada

Hibernate

Asociaciones Entre Entidades

Consultas Jpql Básicas

Hibernate

Consultas Hql Y Jpql

Consultas Jpql Avanzadas

Hibernate

Consultas Hql Y Jpql

Funciones En Consultas Jpql

Hibernate

Consultas Hql Y Jpql

Consultas Criteria Básicas

Hibernate

Api Criteria De Jpa

Consultas Criteria Avanzadas

Hibernate

Api Criteria De Jpa

Carga De Asociaciones En Consultas Con Entitygraph

Hibernate

Api Entitygraph

Carga De Asociaciones En Consultas Con Entitygraph Y Anotaciones

Hibernate

Api Entitygraph

Optimización De Consultas Con Dtos

Hibernate

Optimización

Optimización De Asociaciones Con Carga Lazy

Hibernate

Optimización

Certificados de superación de Hibernate

Supera todos los ejercicios de programación del curso de Hibernate y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender el funcionamiento de las cláusulas JOIN en JPQL para unir diferentes entidades.
  • Aprender a usar GROUP BY para agrupar resultados de consultas por uno o más atributos.
  • Conocer el uso de HAVING para aplicar condiciones sobre los grupos de resultados en combinación con GROUP BY.
  • Entender cómo realizar subconsultas dentro de otras consultas para operaciones complejas de selección y filtrado.