====== Query ====== Hasta ahora nos hemos dedicado a ver las diversas formas de persistencia que soporta hibernate en función de nuestro modelo de objetos de negocio en Java. Pero una característica fundamental de cualquier ORM es la necesidad de leer dichos objetos de la base de datos. Hibernate tiene el objeto org.hibernate.Query|Query que nos da acceso a todas las funcionalidades para poder leer objetos desde la base de datos. Veamos ahora un sencillo ejemplo de cómo funciona y posteriormente explicaremos más funcionalidades de la clase org.hibernate.Query|Query. Query query = session.createQuery("SELECT p FROM Profesor p"); List profesores = query.list(); for (Profesor profesor : profesores) { System.out.println(profesor.toString()); } Lanzar una consulta con Hibernate es bastante simple. Usando la org.hibernate.Session|session llamamos al método org.hibernate.SharedSessionContract#createQuery(java.lang.String)|createQuery(String queryString) con la consulta en formato [[02_hql]] y nos retorna un objeto org.hibernate.Query|Query (Línea 1). Después, sobre el objeto org.hibernate.Query|Query llamamos al método org.hibernate.Query#list()|list() que nos retorna una lista de los objetos que ha retornado (Línea 2). Por último en las líneas de la 4 a la 6 podemos ver cómo usar la lista de objetos ''Profesor'' aunque este código ya es simplemente el uso de la clase java.util.List que no tiene nada que ver con Hibernate. En la siguiente lección se explica en detalle se explica en detalle el lenguaje de consultas que usa Hibernate, ya que no se usa SQL sino [[02_hql|Hibernate Query Languaje (HQL)]] ===== Modelo ===== En los ejemplos que vamos a realizar van a usarse las siguientes clases Java y tablas, que solo mostraremos en formato UML. No vamos a poner el código fuente ni los ficheros de hibernate de mapeo ya que no todas la características que usan han sido explicadas en lecciones anteriores. ==== Modelo de Java ==== El modelo de clases Java es el siguiente: class Profesor Profesor : int id Profesor : String nombre Profesor : String ape1 Profesor : String ape2 ==== Modelo de Tablas ==== El modelo de tablas es el siguiente: class Profesor <> Profesor : INTEGER id Profesor : VARCHAR nombre Profesor : VARCHAR ape1 Profesor : VARCHAR ape2 ===== Query ===== Pasemos ahora a ver las funcionalidades de la clase org.hibernate.Query|Query ==== list() ==== Pasemos ahora a ver con más detalle cómo funciona org.hibernate.Query#list()|list(). === Lista de Objetos === Como ya hemos dicho el método org.hibernate.Query#list()|list() nos retorna una lista con todos los objetos que ha retornado la consulta. En caso de que no se encuentre ningún resultado se retornará una lista sin ningún elemento. Query query = session.createQuery("SELECT p FROM Profesor p"); List profesores = query.list(); for (Profesor profesor : profesores) { System.out.println(profesor.toString()); } === Lista de Array de Objetos === Tan y como veremos en la siguiente lección sobre HQL, también se permite que las consultas retornen datos escalares en vez de clases completas. SELECT p.id,p.nombre FROM Profesor p En la consulta podemos ver cómo en vez de retornar un objeto ''Profesor'' se retorna únicamente el código del profesor y su nombre. En estos casos el método org.hibernate.Query#list()|list() retorna una lista con una Array de objetos con tantos elementos como propiedades hayamos puesto en la ''SELECT''. Veamos ahora un ejemplo. Query query = session.createQuery("SELECT p.id,p.nombre FROM Profesor p"); List listDatos = query.list(); for (Object[] datos : listDatos) { System.out.println(datos[0] + "--" + datos[1]); } * En la línea 1 vemos cómo se crea el objeto org.hibernate.Query|Query con la consulta de datos escalares. * En la línea 2 se ve que el método org.hibernate.Query#list()|list() retorna una lista de array de Objetos.Es decir 'List'. * En la línea 4 se inicia el bucle para recorrer cada una de las filas de datos escalares. * En la línea 5 finalmente se accede a los 2 datos de cada fila mediante ''datos[0]'' y ''datos[1]''. === Lista de Objeto === Hay otro caso cuando hay una única columna en el ''SELECT'' de datos escalares. Es ese caso, como el array a retornar dentro de la lista solo tendría un elemento , no se retorna una lista de arrays ''List'' sino únicamente una lista de elementos ''List''. Si modificamos la anterior consulta de forma que sólo se retorne el nombre, el código quedará de la siguiente forma: Query query = session.createQuery("SELECT p.nombre FROM Profesor p"); List listDatos = query.list(); for (Object datos : listDatos) { System.out.println(datos); } * En la línea 1 ahora la consulta sólo tiene un único dato escalar. * En la línea 2 el método org.hibernate.Query#list()|list() ya no retorna un ''List''. * En la línea 4 se inicia el bucle para recorrer cada una de las filas de datos escalares pero ahora el tipo es ''Object'' en vez de ''Object[]''. * En la línea 5 finalmente se accede al dato sin el índice del array ya que ha dejado de serlo. ==== uniqueResult() ==== En muchas ocasiones una consulta únicamente retornará cero o un resultado. En ese caso es poco práctico que nos retorne una lista con un único elemento. Para facilitarnos dicha tarea Hibernate dispone del método org.hibernate.Query#uniqueResult()|uniqueResult(). Este método retornará directamente el único objeto que ha obtenido la consulta. En caso de que no encuentre ninguno se retornará ''null''. Profesor profesor = (Profesor) session.createQuery("SELECT p FROM Profesor p WHERE id=1001").uniqueResult(); System.out.println("Profesor con Id 1001=" + profesor.getNombre()); Vemos cómo, gracias al método org.hibernate.Query#uniqueResult()|uniqueResult() , se simplifica el código aunque siempre se debe comprobar si ha retornado o no ''null''. Al igual que ocurre con org.hibernate.Query#list()|list() el método org.hibernate.Query#uniqueResult()|uniqueResult() puede retornar tanto un objeto de una entidad , un array de objetos escalares ''Object[]'' o un único objeto escalar ''Object''. Si el método org.hibernate.Query#uniqueResult()|uniqueResult() retorna más de un resultado se producirá la excepción: org.hibernate.NonUniqueResultException: query did not return a unique result ==== Paginación ==== La paginación es parte fundamental de cualquier aplicación ya que una consulta puede tener miles de resultados y no queremos mostrarlos todos a la vez. Para conseguir paginar el resultado de una consulta la clase org.hibernate.Query|Query dispone de los siguientes métodos: * org.hibernate.Query#setMaxResults(int)|setMaxResults(int maxResults): Establece el nº máximo de objetos que van a retornarse. * org.hibernate.Query#setFirstResult(int)|setFirstResult(int firstResult): Establece el primer de los objetos que se van a retornar. Al realizar la paginación son necesarios al menos 2 valores: * El tamaño de la página * El nº de la página a mostrar Con esos 2 valores hemos creado el siguiente código Java que muestra una única página en función del ''tamanyoPagina'' y ''paginaAMostrar''. int tamanyoPagina = 10; int paginaAMostrar = 7; Query query = session.createQuery("SELECT p FROM Profesor p Order By p.id"); query.setMaxResults(tamanyoPagina); query.setFirstResult(paginaAMostrar * tamanyoPagina); List profesores = query.list(); for (Profesor profesor : profesores) { System.out.println(profesor.toString()); } * Las líneas 1 y 2 establecen los valores necesarios para poder mostrar la página, que son el tamaño de la página y el nº de la página a mostrar. * En la línea 4 se crea la org.hibernate.Query|Query. * En la línea 5 se llama al método org.hibernate.Query#setMaxResults(int)|setMaxResults(int maxResults) para indicar que sólo se retornen como máximo tantos objetos como tamaño tiene la página. * En la línea 6 se llama al método org.hibernate.Query#setFirstResult(int)|setFirstResult(int firstResult) para indicarle cuál es el primer objeto a retornar. Este valor coincidirá con el primer objeto de la página que se quiere mostrar. Para calcular dicho valor se multiplica el nº de la página a mostrar por el tamaño de página. Para que esta formula funcione el número de página debe empezar por 0,es decir,que la primera página deberá ser la nº 0, la segunda la nº 1, y así sucesivamente. * Por fín, en la línea 7 se obtienen únicamente los resultados de la página solicitada. * Por último en las líneas 9,10 y 11 se muestran los datos. El mayor problema que tiene la paginación es determinar el nº de páginas para poder mostrárselo al usuario, para saber el nº de páginas es necesario conocer el nº de objetos que retorna la consulta y la forma mas rápida y sencilla es mediante una consulta que las cuente. El siguiente código Java calcula el nº de páginas de la consulta. long numTotalObjetos = (Long) session.createQuery("SELECT count(*) FROM Profesor p").uniqueResult(); int numPaginas =(int) Math.ceil((double)numTotalObjetos / (double)tamanyoPagina); * En la línea 1 realizamos la consulta de ''count(*^)'' para obtener el nº de objetos que retorna la consulta * En la línea 2 de divide el nº total de objetos entre el tamaño de la página obteniéndose el nº total de páginas. Al hacer la división para calcular el nº de páginas es necesario hacer el cast de los 2 valores a ''double'' ya que si no Java automáticamente redondea el resultado a un valor entero con lo que el valor que se le pasa a ''ceil'' ya no será el correcto. ===== Consultas con nombre ===== En cualquier libro sobre arquitectura del software siempre se indica que las consultas a la base de datos no deberían escribirse directamente en el código sino que deberían estar en un fichero externo para que puedan modificarse fácilmente. Hibernate provee una funcionalidad para hacer ésto mismo de una forma sencilla. En cualquier fichero de mapeo de Hibernate se puede incluir el tag '''' con la consulta HQL que deseamos lanzar. En el siguiente ejemplo podemos ver cómo se ha definido una query en el fichero ''Profesor.hbm.xml''. Vemos cómo en las líneas 12, 13, y 14 se ha definido la consulta llamada ''findAllMunicipios'' mediante el tag ''''. ==== Tag ==== Este tag tiene los siguientes datos: * ''name'':Este atributo define el nombre de la consulta.Es el nombre que posteriormente usaremos desde el código Java para acceder a la consulta. * //contenido//:El contenido del tag '''' es la consulta en formato HQL que ejecutará Hibernate. Hacer notar que la consulta se ha incluido dentro de la instrucción ''CDATA'' de XML para evitar que algun simbolo de "''>''" o "''<''" de la consulta se pueda interpretar como cierre del tag ''''. ''CDATA'' forma parte de la especificación de los ficheros XML no siendo algo que se ha definido en Hibernate.Más información en [[wp>CDATA]]. ==== Código Java === Para hacer uso de una consulta con nombre usaremos el método org.hibernate.SharedSessionContract#getNamedQuery(java.lang.String)|getNamedQuery(String queryString) en vez de org.hibernate.SharedSessionContract#createQuery(java.lang.String)|createQuery(String queryString) para obtener el objeto org.hibernate.Query|Query. Por lo tanto sólo se ha modificado la línea 1 y el resto del código queda exactamente igual. Query query = session.getNamedQuery("findAllProfesores"); List profesores = query.list(); for (Profesor profesor : profesores) { System.out.println(profesor.toString()); } Como podemos ver el uso de consultas con nombre es muy sencillo al usar Hibernate.