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. Sin embargo según va creciendo el proyecto podemos tener algunos problemas. 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; } }
Se trata de una entidad muy sencilla que contiene el concepto de Alumno y dos NamedQueries (seleccionarAlumnosNombre, seleccionarAlumnosApellidos). 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?.
Será mucho más sencillo equivocarnos y no saber cuales son los nombres de las consultas correctos. 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.
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.
Otros artículos relacionados : Introducción a JPA ,JPA y MongoDB Relaciones OneToMany
[…] Otros artículos relacionados : ORM.XML , Introducción a JPA , JPA NamedQueries […]
[…] Otros artículos relacionados : JPA OneToMany , JPA First Level Cache , JPA Named Query Organización […]
¿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.… Read more »
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 🙂