JPA NamedQueries es una de las características más utilizadas de JPA ya que nos permite diseñar las consultas en las propias entidades y tenerlas muy a mano. Ademas JPA es el Standard y nos encontraremos con muchisimas aplicaciones desarrolladas con él. Sin embargo según va creciendo el proyecto podemos tener algunos problemas y limitaciones. Vamos a mostrar un posible bloque de código:
package com.arquitecturajava;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
@Entity
@NamedQueries({
@NamedQuery(name="seleccionarAlumnosNombre", query="select a from Alumno a where a.nombre=:nombre"),
@NamedQuery(name="seleccionarAlumnosApellidos", query="select a from Alumno a where a.apellidos=:apellidos")
})
public class Alumno implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String dni;
private String nombre;
private String apellidos;
private int edad;
public Alumno() {
super();
}
public Alumno(String dni, String nombre, String apellidos, int edad) {
super();
this.dni = dni;
this.nombre = nombre;
this.apellidos = apellidos;
this.edad = edad;
}
public String getDni() {
return dni;
}
public void setDni(String dni) {
this.dni = dni;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellidos() {
return apellidos;
}
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
public int getEdad() {
return edad;
}
public void setEdad(int edad) {
this.edad = edad;
}
}
JPA NamedQueries y problemas
Se trata de una entidad muy sencilla que contiene el concepto de Alumno y dos NamedQueries (seleccionarAlumnosNombre, seleccionarAlumnosApellidos). Son consultas muy sencillas de manejar por cualquier programador. Para invocar estas consultas diseñaremos el siguiente programa main:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("UnidadCurso");
EntityManager em = emf.createEntityManager();
TypedQuery<Alumno> consultaAlumnosNombre = em.createNamedQuery(
"seleccionarAlumnosNombre", Alumno.class);
consultaAlumnosNombre.setParameter("nombre", "miguel");
List<Alumno> lista = consultaAlumnosNombre.getResultList();
System.out.println("*************Alumnos*********");
for (Alumno a : lista) {
System.out.println(a.getNombre() + "," + a.getApellidos());
}
TypedQuery<Alumno> consultaAlumnosApellidos = em.createNamedQuery(
"seleccionarAlumnosApellidos", Alumno.class);
consultaAlumnosApellidos.setParameter("apellidos", "alvarez");
List<Alumno> lista2 = consultaAlumnosApellidos.getResultList();
System.out.println("*************Alumnos*********");
for (Alumno a : lista2) {
System.out.println(a.getNombre() + "," + a.getApellidos());
}
em.close();
System.out.println("termino");
Las consultas funcionarán sin problemas. Ahora bien :¿Qué pasará cuando tengamos más clases y más consultas?.

JPA Organización
Será mucho más sencillo equivocarnos y no saber cuales son los nombres de las consultas correctos y acabaremos con mucho lio . Para solventar esta problemática necesitamos organizarnos. El primer paso a realizar será asignar a las consultas un nombre que empiece por la clase a la que pertenecen.
@Entity
@NamedQueries({
@NamedQuery(name="Alumno.seleccionarNombre", query="select a from Alumno a where a.nombre=:nombre"),
@NamedQuery(name="Alumno.seleccionarApellidos", query="select a from Alumno a where a.apellidos=:apellidos")
})
class Alumno {
}
De esta forma será mas fácil organizarse entre cientos de consultas y saber donde se encuentra ubicada cada una de ellas. En segundo lugar podemos convertir el propio nombre de la consulta en una variable estática para que cuando la invoquemos desde un programa main no cometamos errores.

@Entity
@NamedQueries({
@NamedQuery(name=Alumno.BUSCAR_NOMBRE, query="select a from Alumno a where a.nombre=:nombre"),
@NamedQuery(name=Alumno.BUSCAR_APELLIDOS, query="select a from Alumno a where a.apellidos=:apellidos")
})
public class Alumno implements Serializable {
private static final long serialVersionUID = 1L;
public static final String BUSCAR_NOMBRE = "Alumno.seleccionarNombre";
public static final String BUSCAR_APELLIDOS = "Alumno.seleccionarApellidos";
}
De esta forma cuando tengamos que invocar las JPA NamedQueries todo será mucho más fácil y sobre todo mucho más ordenado y con menos posibilidad de cometer errores en el día a día.
TypedQuery<Alumno> consultaAlumnosNombre = em.createNamedQuery(
Alumno.BUSCAR_NOMBRE, Alumno.class);
De esta forma reduciremos los problemas a nivel de nomenclatura dentro de nuestras clases y consultas.
¿Y no se puede hacer una consulta mas genérica? Me explico. Una consulta llamada selectAlumnoByFilter y que le pases un parámetro alumno y la consulta realice el filtro solo de los campos cumplimentados en esa clase?
Por ejemplo. tienes una consulta tipo “select * from alumno where nombre= filtronombre and apellido= filtroapellido”. Si le pasas por parámetro la clase alumno solo con el nombre que dinámicamente solo haga la consulta con ese valor “select * from alumno where nombre= filtronombre”.
Seguro que muchos conocéis el truco de poner where 1=1 y luego podías añadir los filtros “and …” que quisieras. Con un método era suficiente para hacer una consulta de cualquier campo de una tabla jugando con los valores null.
Mi ultima frase es para felicitar a Cecilio por los fantásticos contenidos de la web.
Si puedes hacerlo perfectamente creando una query dinámica. Los named queries están bien pero tienen sus limitaciones 🙂
El enfoque que has puesto es el que más se utiliza por cuestiones de legibilidad. Los DAO ya no vienen mucho al caso, cada vez son usados menos. En JEE, preferimos EJB y colapsamos dos capas.
Saludos y gracias por la entrada.
Es cierto que no todo el mundo las apoya . Pero siguen teniendo su hueco ya que aportan una gran flexibilidad y recordemos que las capas DAO.. o suelen ser repositorios y no siempre tienen que ser contra base de datos.
Esta bien esta aproximación siempre que ataques a una sola tabla en la consulta. En cuanto tengas que hacer joins con otras tablas esta aproximación ya no es valida. Mejor tener una capa repository que tenga inyectados los daos. En mi humilde opinion.
No me he explicado bien Alonso. Evidentemente la capa de repositorio puedes seguir teniendola 🙂 solamente que ahora no cometerás errores de nombres en ningún sitio ya que saltarían en tiempo de compilación 🙂
Me corrijo es cierto en los DAO no aportarían tanto 🙂
Agregando al comentario anterior:
…sobre todo teniendo en cuenta que Spring Data abstrae de la capa de datos y pone las querys en las anotaciones en la capa repository.
Spring Data es todavía más generalista , pero necesitas usar Spring , JPA es un estandar
Que ventajas tiene usar NamedQueries vs tener las query en la capa repository?
Saludos
Están precompiladas y se evitan repeticiones y acoplamientos escesivos
Creo que otra buena idea acompañada de esta, es tener los NamedQueries organizados en xmls, buena idea la de las variables estaticas.
Hola Jesús, me parece muy interesante tener organizados en un fichero externo las NamedQuerys.
De esa forma no perderías todas las consultas que has escrito, si por alguna razón generas de nuevo la clase.
Por ejemplo cuando añades un campo nuevo a una tabla, me ha ocurrido en ocasiones que he perdido las namedQuerys, al regenerar automáticamente la clase de nuevo desde Eclipse.
Si a veces la generación automática tiene esas cosillas , en esos casos tenerlas aparte a través del orm.xml puede aportar 🙂