Tipos de consultas JPA

1. Información general

En este tutorial, discutiremos los diferentes tipos de consultas JPA. Además, nos centraremos en comparar las diferencias entre ellos y ampliar los pros y los contras de cada uno.

2. Configuración

En primer lugar, definamos la clase UserEntity que usaremos para todos los ejemplos de este artículo:

@Table(name = "users") @Entity public class UserEntity { @Id private Long id; private String name; //Standard constructor, getters and setters. }

Hay tres tipos básicos de consultas JPA:

  • Consulta , escrita en la sintaxis de Java Persistence Query Language (JPQL)
  • NativeQuery , escrito en sintaxis SQL simple
  • Consulta API de criterios , construida mediante programación a través de diferentes métodos

Explorémoslos.

3. Consulta

Una consulta es similar en sintaxis a SQL, y generalmente se usa para realizar operaciones CRUD:

public UserEntity getUserByIdWithPlainQuery(Long id) { Query jpqlQuery = getEntityManager().createQuery("SELECT u FROM UserEntity u WHERE u.id=:id"); jpqlQuery.setParameter("id", id); return (UserEntity) jpqlQuery.getSingleResult(); }

Esta consulta recupera el registro coincidente de la tabla de usuarios y también lo asigna al objeto UserEntity .

Hay dos subtipos de consultas adicionales :

  • TypedQuery
  • NamedQuery

Veámoslos en acción.

3.1. TypedQuery

Debemos prestar atención a la declaración de devolución de nuestro ejemplo anterior. JPA no puede deducir cuál será el tipo de resultado de la consulta y, como resultado, tenemos que transmitir.

Pero JPA proporciona un subtipo de consulta especial conocido como TypedQuery. Esto siempre es preferible si conocemos nuestro tipo de resultado de consulta de antemano. Además, hace que nuestro código sea mucho más confiable y más fácil de probar.

Veamos una alternativa de TypedQuery , en comparación con nuestro primer ejemplo:

public UserEntity getUserByIdWithTypedQuery(Long id) { TypedQuery typedQuery = getEntityManager().createQuery("SELECT u FROM UserEntity u WHERE u.id=:id", UserEntity.class); typedQuery.setParameter("id", id); return typedQuery.getSingleResult(); }

De esta manera, obtenemos una escritura más fuerte de forma gratuita, evitando posibles excepciones de conversión en el futuro.

3.2. NamedQuery

Si bien podemos definir dinámicamente una consulta en métodos específicos, eventualmente pueden convertirse en una base de código difícil de mantener. ¿Qué pasaría si pudiéramos mantener las consultas de uso general en un lugar centralizado y fácil de leer?

JPA también nos ayudó a cubrir este tema con otro subtipo de consulta conocido como NamedQuery.

Definimos NamedQuery en la propia clase Entity , proporcionando una forma centralizada, rápida y fácil de leer y encontrar las consultas relacionadas con la entidad .

Todas las NamedQueries deben tener un nombre exclusivo.

Veamos cómo podemos agregar una NamedQuery a nuestra clase UserEntity :

@Table(name = "users") @Entity @NamedQuery(name = "UserEntity.findByUserId", query = "SELECT u FROM UserEntity u WHERE u.id=:userId") public class UserEntity { @Id private Long id; private String name; //Standard constructor, getters and setters. }

La anotación @NamedQuery tiene que agruparse dentro de una anotación @NamedQueries si usamos Java antes de la versión 8. A partir de Java 8 en adelante, podemos simplemente repetir la anotación @NamedQuery en nuestra clase Entity .

Usar una NamedQuery es muy simple:

public UserEntity getUserByIdWithNamedQuery(Long id) { Query namedQuery = getEntityManager().createNamedQuery("UserEntity.findByUserId"); namedQuery.setParameter("userId", id); return (UserEntity) namedQuery.getSingleResult(); }

4. NativeQuery

Un NativeQuery es simplemente una consulta SQL. Estos nos permiten liberar todo el poder de nuestra base de datos, ya que podemos usar funciones propietarias que no están disponibles en la sintaxis restringida por JPQL.

This comes at a cost. We lose database portability of our application with NativeQuery because our JPA provider can't abstract specific details from the database implementation or vendor anymore.

Let's see how to use a NativeQuery that yields the same results as our previous examples:

public UserEntity getUserByIdWithNativeQuery(Long id) { Query nativeQuery = getEntityManager().createNativeQuery("SELECT * FROM users WHERE id=:userId", UserEntity.class); nativeQuery.setParameter("userId", id); return (UserEntity) nativeQuery.getSingleResult(); }

We must always consider if a NativeQuery is the only option. Most of the time, a good JPQL Query can fulfill our needs and most importantly, maintain a level of abstraction from the actual database implementation.

Using NativeQuery doesn't necessarily mean locking ourselves to one specific database vendor. After all, if our queries don't use proprietary SQL commands and are using only a standard SQL syntax, switching providers should not be an issue.

5. Criteria API Query

Criteria API queries are programmatically-built, type-safe queries – somewhat similar to JPQL queries in syntax:

public UserEntity getUserByIdWithCriteriaQuery(Long id) { CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder(); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(UserEntity.class); Root userRoot = criteriaQuery.from(UserEntity.class); UserEntity queryResult = getEntityManager().createQuery(criteriaQuery.select(userRoot) .where(criteriaBuilder.equal(userRoot.get("id"), id))) .getSingleResult(); return queryResult; }

It can be daunting to use Criteria API queries first-hand, but they can be a great choice when we need to add dynamic query elements or when coupled with the JPA Metamodel.

6. Conclusion

In this quick article, we learned what JPA Queries are, along with their usage.

Las consultas JPA son una excelente manera de abstraer nuestra lógica comercial de nuestra capa de acceso a datos, ya que podemos confiar en la sintaxis JPQL y dejar que nuestro proveedor JPA de elección maneje la traducción de consultas .

Todo el código presentado en este artículo está disponible en GitHub.