Skip to content

4 Java Persistence Query Language (JPQL)

Introducción

Java Persistence Query Language (JPQL) es un lenguaje de consulta que se utiliza en el contexto de Java Persistence API (JPA) para realizar consultas en bases de datos relacionales de una manera orientada a objetos. JPQL se basa en conceptos de programación orientada a objetos y abstrae las diferencias entre los diversos sistemas de bases de datos, permitiendo a los desarrolladores realizar consultas de manera independiente del sistema de gestión de bases de datos subyacente. A continuación, se detallan los aspectos más importantes de JPQL:

  • Sintaxis Similar a SQL: JPQL tiene una sintaxis similar a SQL, lo que facilita la transición para aquellos familiarizados con SQL. Sin embargo, en lugar de referirse a tablas y columnas, se hace referencia a clases y atributos de entidades.
  • Consulta de Entidades: JPQL permite realizar consultas en las entidades de tu aplicación en lugar de tablas de la base de datos. Las entidades se mapean a tablas en la base de datos, y JPQL ofrece una abstracción para interactuar con estas entidades.

Consultas JPQL

En JPQL encontramos los tipos de consultas similares a SQL:

  • SELECT: Se utiliza para recuperar datos de la base de datos en forma de objetos de entidad.
  • UPDATE: Permite realizar actualizaciones en los registros de la base de datos.
  • DELETE: Permite eliminar registros de la base de datos.
  • INSERT (rara vez utilizado en JPQL): Se usa para insertar nuevos registros.

Al hacer un SELECT tenemos que tener en cuenta las siguientes cláusulas

  • SELECT: Indica qué atributos o entidades se deben recuperar.
  • FROM: Especifica la entidad (o alias) desde la que se realizará la consulta.
  • WHERE: Define condiciones para filtrar los resultados.
  • ORDER BY: Ordena los resultados de la consulta.
  • GROUP BY y HAVING: Utilizados para agrupar y filtrar resultados, respectivamente.
SELECT p FROM Product p

En el ejemplo anterior obtenemos todos los registros de la entidad Producto, siendo P un alias para indicar todos los atributos de la entidad producto

En JPQL, podemos hacer uso de los parámetros de tal forma que facilita la creación de consultas dinámicas. Un parámetro va precedidos por dos puntos : y el nombre del parámetro (:parámetro). Hay que indicarle el valor del parámetro antes de ejecutar la consulta.

SELECT p FROM Product p WHERE p.name = :productName

JPQL también admite funciones de agregación comunes como, COUNT, SUM, AVG, MIN y MAX, así como las palabras claves para las asociaciones: join, inner join, etc.

SELECT p, c FROM Product p JOIN p.category c

A través de JPQL puedes realizar subconsultas al igual que en SQL:

SELECT COUNT(p) FROM Product p WHERE p.category.name = 'Electrónicos'

Interfaz Query y TypeQuery

En Java Persistence API (JPA), TypedQuery y Query son dos interfaces relacionadas que se utilizan para ejecutar consultas, pero tienen diferencias clave en términos de seguridad de tipos y conveniencia.

  1. TypeQuery<T>:
    • Es una interfaz genérica que se utiliza para consultas JPQL que deben devolver resultados tipados
    • Proporciona seguridad de tipos, lo que significa que el tipo de resultado se conoce en tiempo de compilación y se garantiza que coincida con el tipo especificado en la consulta
    • Mejora la legibilidad y el mantenimiento del código al permitir el uso de resultados tipados en lugar de objetos genéricos.
    • Es preferible cuando se espera un tipo de resultado específico y se desea evitar conversiones manuales.
  2. Query:
    • Es una interfaz no genérica que se utiliza para consultas JPQL que pueden devolver resultados no tipados o cuando el tipo de resultado no se conoce de antemano.
    • No proporciona seguridad de tipos en tiempo de compilación, lo que significa que los resultados se devuelven como objetos genéricos (Object).
    • Es menos seguro en términos de tipos y puede requerir conversiones manuales para trabajar con los resultados.
    • Es útil en casos donde la estructura de los resultados es desconocida o cuando se desea una mayor flexibilidad.

En resumen, la principal diferencia entre TypedQuery y Query radica en la seguridad de tipos. TypedQuery se utiliza cuando se espera un tipo de resultado específico y se desea seguridad de tipos, mientras que Query se utiliza cuando la estructura de los resultados es menos predecible o no se necesita seguridad de tipos. La elección entre ambas depende de los requisitos de tu consulta y de cuánto control deseas tener sobre los tipos de resultados.

Es importante resaltar que TypeQueryes hijo directo de Query.

Ejecución de consultas

Para poder ejecutar las consultas en Java, se podrá usar el método createQuery(query) que recibe la query como parámetro. Algunos de sus métodos importantes son:

  • Query getResultList(): Ejecuta la consulta y devuelve una lista con los registros resultados.
  • Query getSingleResult(): Ejecuta la consulta y devuelve un único resultado. Lanza la excepción NoResultException si la consulta no devuelve ningún resultado y la excepción NonUniqueResultException si la consulta devuelve más de un resultado.
  • Query setParameter(String name, Object value): establece los valores de los parámetros creados en la consulta. Si hemos creado un parámetro en la consulta :name en el parámetro del método se indica unicamente el nombre y no los dos puntos. Existe un versión sobrecargada que en vez de recibir el nombre del parámetro recibe la posición del mismo.
  • Query setFirstResult(int startPosition): se utiliza para establecer el primer resultado que se debe recuperar. Es útil para la paginación de resultados.
  • Query setMaxResults(int maxResult): Este método se utiliza para limitar el número de resultados recuperados. También es útil para paginación.
  • int executeUpdate(): Este método se utiliza para ejecutar una consulta de modificación (por ejemplo, UPDATE o DELETE). Devuelve el número de registros afectados por la consulta.

En JPQL podemos ejecutar consultas nombradas. Una consulta nombradas es un consulta ya creada e identificada con un nombre, por lo que primero debe estar definida en el archivo de mapeo, o por el contrario a través de las anotaciones:

<named-query name="Product.findByName">
    <query>
        SELECT p FROM Product p WHERE p.name = :productName
    </query>
</named-query>
@Entity
@NamedQuery(
    name = "Product.findByName",
    query = "SELECT p FROM Product p WHERE p.name = :productName"
)
public class Product {
    // Atributos, constructores y métodos de la entidad
}

Para usarlo en java hacemos uso del método createNamedQuery('name'), siendo name el nombre de la consultada creada:

TypedQuery<Product> query = entityManager.createNamedQuery("Product.findByName", Product.class);
query.setParameter("productName", "Laptop");
List<Product> products = query.getResultList();

Consultas nativas

Las consultas nativas en JPA permiten ejecutar consultas SQL directamente en la base de datos utilizando SQL nativo en lugar de JPQL. Las consultas nativas son útiles cuando necesitas ejecutar consultas SQL específicas de tu base de datos o realizar operaciones que no se pueden expresar fácilmente en JPQL.

Para ejecutar una consulta nativa en JPA, puedes usar el método createNativeQuery() proporcionado por la interfaz EntityManager. A continuación, se muestra un ejemplo de cómo ejecutar una consulta SQL nativa en JPA:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("MyPersistenceUnit");
EntityManager em = emf.createEntityManager();

// Ejecutar una consulta SQL nativa para seleccionar todos los productos con un precio mayor a 50
String sql = "SELECT * FROM Product WHERE price > 50";
List<Object[]> results = em.createNativeQuery(sql).getResultList();

for (Object[] row : results) {
    Long id = (Long) row[0];
    String name = (String) row[1];
    double price = (Double) row[2];
    System.out.println("Product ID: " + id + ", Name: " + name + ", Price: " + price);
}

em.close();
emf.close();