Consultas JPQL con @Query en repositorios
JPQL (Java Persistence Query Language) es el lenguaje de consultas estándar de JPA que nos permite escribir consultas orientadas a objetos en lugar de SQL tradicional. Spring Data JPA integra JPQL a través de la anotación @Query, permitiéndonos definir consultas personalizadas directamente en nuestros repositorios.
¿Te está gustando esta lección?
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
A diferencia de las consultas derivadas que se basan en nombres de métodos, @Query nos ofrece control total sobre la consulta que queremos ejecutar. Esto resulta especialmente útil cuando necesitamos consultas complejas que serían difíciles de expresar mediante convenciones de nombres.
Sintaxis básica de JPQL
JPQL utiliza nombres de entidades y sus propiedades en lugar de nombres de tablas y columnas. Las consultas se escriben contra el modelo de objetos, no contra el esquema de base de datos:
@Repository
public interface ProductoRepository extends JpaRepository<Producto, Long> {
@Query("SELECT p FROM Producto p WHERE p.precio > :precio")
List<Producto> findProductosCaros(@Param("precio") BigDecimal precio);
}
En este ejemplo, Producto
hace referencia a la entidad JPA, no a la tabla de base de datos. La consulta utiliza el parámetro nombrado :precio
que se mapea con el parámetro del método mediante @Param
.
Parámetros en consultas JPQL
Existen dos formas principales de pasar parámetros a las consultas JPQL: parámetros posicionales y parámetros nombrados.
Parámetros posicionales utilizan números precedidos por ?
:
@Query("SELECT p FROM Producto p WHERE p.categoria = ?1 AND p.stock > ?2")
List<Producto> findByCategoriaAndStock(String categoria, Integer stock);
Parámetros nombrados utilizan nombres precedidos por :
y son más legibles:
@Query("SELECT p FROM Producto p WHERE p.categoria = :categoria AND p.stock > :stockMinimo")
List<Producto> findByCategoriaAndStock(
@Param("categoria") String categoria,
@Param("stockMinimo") Integer stockMinimo
);
Los parámetros nombrados son la práctica recomendada porque hacen el código más mantenible y menos propenso a errores.
Consultas de selección con proyecciones
JPQL permite seleccionar campos específicos en lugar de entidades completas, lo que mejora el rendimiento cuando solo necesitamos ciertos datos:
@Query("SELECT p.nombre, p.precio FROM Producto p WHERE p.categoria = :categoria")
List<Object[]> findNombreYPrecioPorCategoria(@Param("categoria") String categoria);
Para mayor type safety, podemos crear interfaces de proyección:
public interface ProductoResumen {
String getNombre();
BigDecimal getPrecio();
String getCategoria();
}
@Query("SELECT p.nombre as nombre, p.precio as precio, p.categoria as categoria " +
"FROM Producto p WHERE p.activo = true")
List<ProductoResumen> findProductosActivosResumen();
Consultas con JOIN
JPQL soporta diferentes tipos de JOIN para consultar datos relacionados:
@Query("SELECT p FROM Producto p JOIN p.categoria c WHERE c.nombre = :nombreCategoria")
List<Producto> findProductosPorNombreCategoria(@Param("nombreCategoria") String nombreCategoria);
@Query("SELECT p FROM Producto p LEFT JOIN p.reviews r WHERE r.puntuacion > :puntuacion")
List<Producto> findProductosConBuenasReviews(@Param("puntuacion") Integer puntuacion);
Consultas de modificación
Para operaciones de actualización y eliminación, necesitamos usar @Modifying
junto con @Query
:
@Modifying
@Query("UPDATE Producto p SET p.precio = p.precio * :factor WHERE p.categoria = :categoria")
int actualizarPreciosPorCategoria(
@Param("factor") BigDecimal factor,
@Param("categoria") String categoria
);
@Modifying
@Query("DELETE FROM Producto p WHERE p.stock = 0 AND p.fechaCreacion < :fecha")
int eliminarProductosSinStockAntiguos(@Param("fecha") LocalDateTime fecha);
Es importante recordar que las consultas de modificación deben ejecutarse dentro de una transacción, típicamente usando @Transactional
en el método del servicio que las invoca.
Consultas con ordenación y paginación
JPQL integra perfectamente con Pageable para implementar paginación:
@Query("SELECT p FROM Producto p WHERE p.precio BETWEEN :precioMin AND :precioMax ORDER BY p.precio ASC")
Page<Producto> findProductosEnRangoPrecio(
@Param("precioMin") BigDecimal precioMin,
@Param("precioMax") BigDecimal precioMax,
Pageable pageable
);
También podemos especificar ordenación directamente en la consulta:
@Query("SELECT p FROM Producto p WHERE p.categoria = :categoria ORDER BY p.fechaCreacion DESC, p.nombre ASC")
List<Producto> findProductosPorCategoriaOrdenados(@Param("categoria") String categoria);
Funciones agregadas y agrupación
JPQL soporta funciones agregadas como COUNT, SUM, AVG, MAX y MIN:
@Query("SELECT COUNT(p) FROM Producto p WHERE p.categoria = :categoria")
Long contarProductosPorCategoria(@Param("categoria") String categoria);
@Query("SELECT p.categoria, AVG(p.precio) FROM Producto p GROUP BY p.categoria")
List<Object[]> obtenerPrecioPromedioPorCategoria();
@Query("SELECT p.categoria, COUNT(p) FROM Producto p GROUP BY p.categoria HAVING COUNT(p) > :minimo")
List<Object[]> obtenerCategoriasConMinimoProductos(@Param("minimo") Long minimo);
Consultas con subconsultas
JPQL permite subconsultas para lógica más compleja:
@Query("SELECT p FROM Producto p WHERE p.precio > " +
"(SELECT AVG(p2.precio) FROM Producto p2 WHERE p2.categoria = p.categoria)")
List<Producto> findProductosConPrecioSuperiorALaMedia();
@Query("SELECT p FROM Producto p WHERE p.id IN " +
"(SELECT v.producto.id FROM Venta v WHERE v.fecha > :fecha)")
List<Producto> findProductosVendidosDespuesDe(@Param("fecha") LocalDateTime fecha);
Ventajas de usar @Query con JPQL
El uso de @Query con JPQL ofrece varios beneficios importantes. Proporciona flexibilidad total para escribir consultas complejas que serían imposibles con consultas derivadas. Mantiene la portabilidad entre diferentes bases de datos al usar JPQL en lugar de SQL nativo. Ofrece type safety al trabajar con entidades y sus propiedades, y permite optimización mediante proyecciones y consultas específicas.
Esta aproximación resulta ideal cuando necesitamos consultas que van más allá de las capacidades de las consultas derivadas, manteniendo al mismo tiempo la elegancia y simplicidad del ecosistema Spring Data JPA.
Aprendizajes de esta lección
- Comprender la sintaxis y uso básico de JPQL en Spring Data JPA.
- Aprender a definir consultas personalizadas con @Query y manejar parámetros nombrados y posicionales.
- Implementar proyecciones y consultas con JOIN para optimizar la recuperación de datos.
- Realizar consultas de modificación con @Modifying y gestionar transacciones adecuadamente.
- Aplicar paginación, ordenación, funciones agregadas y subconsultas en JPQL para consultas complejas.
Completa SpringBoot y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs