====== Consultas en beans de entidad ====== --- //[[pedrody@um.es|PEDRO DELGADO YARZA]] 2014/02/06 08:53// A la hora de desarrollar aplicaciones, surge la necesidad de realizar consultas a base de datos para recuperar objetos basados en diversos criterios. Dichas consultas en el pasado se han basado netamente en ejecutar consultas nativas SQL de base de datos y mapeando manualmente a clases java, a posteriori, dichos resultados. Actualmente, el motor de persistencia nos provee de una manera algo más cómoda de recuperar esos datos, ahorrándonos el mapeo manual de valores y simplificando la manera de realizar las [[http://docs.jboss.org/hibernate/core/4.2/manual/en-US/html/ch16.html|consultas JPQL]] o consultas nativas SQL. No obstante se nos permite seguir realizando dichas búsquedas de la manera anterior, este hecho hace que distingamos entre dos tipos de consultas: * **NamedQuery**: Consultas escritas en el lenguaje proporcionado por el motor de persistencia, en el caso de Hibernate el HQL. * **NamedNativeQuery**: Consultas escritas en lenguaje SQL nativo. Utilizando cualquiera de los tipos anteriores podemos lanzar nuestras consultas a base de datos, no obstante la configuración necesaria para cada una difiere, como veremos en apartados sucesivos. =====Definición de Queries===== Una vez que queremos empezar a preparar consultas de base de datos debemos decidir dónde definirlas. En este punto existen dos puntos básicos donde eñnglobarlas: * **Consultas declaradas dentro de un bean de entidad**: Estas consultas se declaran en la propia clase del bean de entidad y tienen como requisito que los objetos que devuelva sean del tipo del bean. * **Consultas declaradas en el fichero orm.xml**: Estas consultas se declaran en un fichero de configuración **orm.xml** y pueden devolver o no beans de entidad. En este caso no se aplica ninguna restrucción concreta. **Consultas declaradas dentro de un bean de entidad** Están contenidas dentro de la clase del bean al que representan y escritas en código HQL. Se disponen en la cabecera de la clase dentro de la anotación **@NamedQueries** o **NamedNativeQueries**, indicando cada query con la anotación **@NamedQuery** o **@NamedNativeQuery** según si queremos escribirlas en HQL o SLQ. Un ejemplo con HQL sería: @Entity @NamedQueries({ @NamedQuery(name = "anuncioFiltrado", query = "select an from Anuncio an where an.id like :id and lower(an.descripcion) like :descripcion and lower(an.nombre) like :nombre and ((:fechaPublicacion is null) OR (an.fechaPublicacion=:fechaPublicacion))"), @NamedQuery(name = "totalAnuncioFiltrado", query = "select count(an) from Anuncio an where an.id like :id and lower(an.descripcion) like :descripcion and lower(an.nombre) like :nombre and ((:fechaPublicacion is null) OR (an.fechaPublicacion=:fechaPublicacion))") }) public class Anuncio implements Serializable { ... ... } Como podemos ver, necesitamos hacer uso de dos anotaciones: * **NamedQueries**: Especifica una lista separada por comas de consultas HQL. * **NamedQuery**: Representa una consulta HQL. Sus propiedades son: * **name**: Nombre de la consulta. Este nombre lo utilizaremos a la hora de buscarla para lanzarla. * **query**: Consulta en HQL. * **hints**: Propiedades adicionales que queramos darle a la consulta. De igual manera, si quisiéramos especificarlas en SQL: @NamedNativeQueries( { @NamedNativeQuery(name = "consultaAnunciosNativa", query = "SELECT * FROM ANUNCIO", resultClass=Anuncio.class) }) Al igual que antes necesitamos hacer uso de dos anotaciones: * **NamedNativeQueries**: Especifica una lista separada por comas de consultas SQL. * **NamedNativeQuery**: Representa una consulta SQL. Sus propiedades son: * **name**: Nombre de la consulta. Este nombre lo utilizaremos a la hora de buscarla para lanzarla. * **query**: Consulta en SQL. * **resultClass**: Clase a la que se ha de mapear el resultado de la consulta. **Consultas declaradas en el fichero orm.xml** Las consultas anteriores, pueden realizarse registrarse en vez de en el propio bean, en el fichero de configuración orm.xml mediante tags xml. En el caso de las consultas HQL haremos uso del tag **named-query** select anuncio from Anuncio anuncio Como vemos, las propiedades que requiere son las mismas que son necesarias en el caso anterior, teniendo en el tag **** la consulta, y en la lista de tags **** las propiedades adicionales. En el caso de que quisiéramos hacer la consulta en SLQ nativo, usaríamos el tag **named-native-query** procediendo de la siguiente manera: SELECT * FROM ANUNCIO Las propiedades adicionales siguen el mismo esquema que el caso anterior. =====Consultas que no devuelven todos los campos de un Bean de Entidad===== Si queremos realizar una consulta que sólo devuelva un subconjunto de los campos de una tabla, o que devuelva campos de tablas distintas debemos realizar las consultas de dos posibles maneras: * Escribir una named-query que devuelva sólo los campos que queremos. El resultado de esta consulta devolverá un **ArrayList** donde cada elemento del ArrayList será una fila de la consulta (un par id/nombre), contenidos dentro de un Object[] que contendrá los diferentes valores de la fila. Para acceder a estos valores lo haremos en forma de array Object[0], Object[1], ... select anuncio.id , anuncio.nombre from Anuncio anuncio * Escribir una named-native-query (SQL) que devuelva dos campos. (id y nombre) y especificar (obligatorio) un result-set-mapping a columnas, como mostramos a continuación. SELECT anuncio.ID id, anuncio.NOMBRE nombre FROM FUNDEWEB.ANUNCIO anuncio También podemos indicar esto en el propio Bean de la forma: @NamedNativeQueries( { @NamedNativeQuery(name = "consultaAnunciosParcial", query = "SELECT anuncio.ID id, anuncio.NOMBRE nombre FROM FUNDEWEB.ANUNCIO anuncio", resultSetMapping = "mappingAnuncioColumn") }) @SqlResultSetMapping(name = "mappingAnuncioColumn", columns = { @ColumnResult(name = "id"), @ColumnResult(name = "nombre") }) **Importante:** Para utilizar este mapeo en la consulta es necesario que utilicemos alias de las columnas de la consulta que devolverá un ArrayList. =====Establecer mapeos para el resultado devuelto por la consulta===== Cuando utilizamos named-native-query es posible usar un sql-result-set-mapping para asociar el nombre de las columnas en la tabla a otro diferente en el de las propiedad en el bean de entidad. Para realizar esta tarea disponemos del tag **field-result** con las propiedades: * **name**: Nombre que quiero que tenga la columna resultado. * **column**: Nombre de la columna real que se devuelve tras la query. =====Pasar parámetros a la consulta===== Para pasar parámetros a la consulta simplemente escribir el parámetro a pasar con dos puntos antes y posteriormente sustituirlo a la hora de hacer la consulta. **En NamedQuery** @NamedQueries({ @NamedQuery(name = "anuncioFiltrado", query = "select an from Anuncio an where an.id = :id" }) public class Anuncio implements Serializable { ... } **En fichero orm.xml** select anuncio from Anuncio anuncio where anuncio.id = :id =====Consultas sobre procedimientos===== En versiones anteriores de Fundeweb (1.2.x) las consultas se realizaban vía orm.xml, no obstante, tras el desarrollo de [[fdw2.0:fundeweb2.0:gt:generacion_de_pao_s_usando_genetica|Genética]] las llamadas a funciones y procedimientos han de realizarse pasando por este mecanismo, ya que es más seguro y facilita a los desarrolladores las llamadas, minimizando los errores a la hora de pasar parámetros. =====Como ejecutar las consultas===== Una vez definido cómo especificamos las consultas vamos a mostrar la manera de llamar a cada una de ellas //Esta consulta es una query que devuelve directametne los beans de entidad, esta consulta podría ser una NamedQuery o NativeNamedQuery Query query = entityManager.createQuery("consultaAnuncios"); List lista = query.getResultList(); //Consulta con paso de parámetros Query query2 = entityManager.createNamedQuery("procedimientoComentario"); query.setParameter("VAR", "juan"); List lista2 = query.getResultList(); //Consulta con un mapeo que devuelve una lista de Object[] Query query3 = entityManager.createNamedQuery("consultaAnunciosParcialHQL"); List lista3 = query.getResultList(); =====Clase QueryUtils===== Es una clase utilidad que nos permite realizar mapeos y transformaciones que las clases de Hibernate no pueden realizar, solo se puede utilizar con consultas nativas SQL. Así pues podemos: * Mapear el resultado de una Query en un Map formado por "nombre propiedad" y valor, en vez de la lista de Object[] que devuelve por defecto el motor de persistencia. List lista5 = QueryUtil.getMapFromNamedNativeQuery("consultaAnunciosParcial",entityManager); for (Map map:lista5){ BigDecimal id = (BigDecimal) map.get("id"); String nombre = (String) map.get("nombre"); } * Mapear directamente al objeto destino que queremos tener. Para ello es imprescindible que cada atributo de la clase de mapeo destino tenga su get y su set. List lista6 = QueryUtil.getDtosFromNamedNativeQuery("consultaAnunciosParcial",entityManager,AnuncioDto.class);