HQL: El Lenguaje de Consulta de Hibernate Hibernate está equipado con un lenguaje de consulta extremadamente potente que (intencionalmente en absoluto) se parece muchísimo a SQL. Pero no te engañes por la sintaxis; HQL es completamente orientado a objetos, entendiendo nociones como herencia, polimorfismo y asociación. Sensibilidad a Mayúsculas Las consultas son insensibles a mayúsculas, excepto para nombres de clases Java y propiedades. De modo que SeLeCT es lo mismo que sELEct e igual a SELECT, pero org.hibernate.eg.FOO no lo es a org.hibernate.eg.Foo y foo.barSet no es igual a foo.BARSET. Este manual usa palabras clave HQL en minúsculas. Algunos usuarios encuentran las consultas con palabras clave en mayúsculas más leíbles, pero encontramos esta convención fea cuando se encaja en código Java. La cláusula from La consulta más simple posible de Hibernate es de la forma: que simplemente devuelve todas las instancias de la clase eg.Cat. Usualmente no necesitamos cualificar el nombre de la clase, ya que auto-import está por defecto. De modo que casi siempre escribimos solamente: La mayoría del tiempo, necesitarás asignar un alias, ya que querrás referirte al Cat en otras partes de la consulta. Esta consulta asigna el alias cat a las instancias de Cat, de modo que podríamos usar ese alias luego en la consulta. La palabra clave as es opcional; también podríamos escribir: Pueden aparecer múltiples clases, resultando en un producto cartesiano o unión "cruzada" (cross join). Se considera buena práctica el nombrar los alias de consulta usando una inicial en minúsculas, consistente con los estándares de nombrado de Java para variables locales (por ejemplo, domesticCat). Asociaciones y uniones (joins) Podemos también asignar aliases a entidades asociadas, e incluso a elementos de una colección de valores, usando una join. Los tipos de join soportados son prestados de ANSI SQL inner join left outer join right outer join full join (no útil usualmente) Las construcciones inner join, left outer join y right outer join pueden ser abreviadas. Puedes proveer condiciones de unión extra usando la palabra clave with de HQL. 10.0]]> En adición, un "fetch" join permite a las asociaciones o colecciones de valores ser inicializadas junto a sus objetos padres, usando una sola selección. Esto es particularmente útil en el case de una colección. Efectivamente sobrescribe el outer join y las declaraciones perezosas (lazy) del fichero de mapeo para asociaciones y colecciones. Ver para más información. Usualmente a un fetch join no se necesita asignársele un alias, porque los objetos asociados no deben ser usados en la cláusula where (ni en cualquier otra cláusula). Además, los objetos asociados no son devueltos directamente en los resultados de consulta. En cambio, pueden ser accedidos vía el objeto padre. La única razón por la que necesitaríamos un alias es estamos uniendo recursivamente otra colección: Nota que la construcción fetch no puede usarse en consultas llamadas usando scroll() o iterate(). Ni debe usarse fetch junto con setMaxResults() o setFirstResult(). Tampoco puede usarse fetch junto a una condición with ad hoc. Es posible crear un producto cartesiano trayendo por join más de una colección en una colección, así que ten cuidado en este caso. Traer por join múltiples roles de colección también da a veces resultados inesperados para mapeos de bag, así que sé cuidadoso sobre cómo formular tus consultas en este caso. Finalmente, nota que full join fetch y right join fetch no son significativos. Si estás usando recuperación perezosa a nivel de propiedad (con instrumentación de bytecode), es posible forzar a Hibernate a traer las propiedades perezosas inmediatamente (en la primera consulta) usando fetch all properties. La cláusula select La cláusula select escoge qué objetos y propiedades devolver in el conjunto resultado de la consulta. Considera: La consulta seleccionará mates de otros Cats. Realmente, puedes expresar esta consulta en un forma más compacta como: Las consultas pueden devolver propiedades de cualquier tipo de valor incluyendo propiedades de tipo componente: Las consultas pueden devolver múltiples objetos y/o propiedades como un array de tipo Object[], o como una List, o como un objeto real Java de tipo seguro, asumiendo que la clase Family tiene un constructor apropiado. Puedes asignar aliases para seleccionar expresiones usando as: Esto es lo más útil cuando se usa junto con select new map: Esta consulta devuelve un Map de aliases a valores seleccionados. Funciones de agregación Las consultas HQL pueden incluso devolver resultados de funciones de agregación sobre propiedades: Las funciones de agregación soportadas son avg(...), sum(...), min(...), max(...) count(*) count(...), count(distinct ...), count(all...) Puedes usar operadores aritméticos, concatenación, y funciones SQL reconocidas en la cláusula select: Las palabras clave distinct y all pueden ser usadas y tienen las misma semántica que en SQL. Consultas polimórficas Una consulta como: devuelve instancias no sólo de Cat, sino también de subclases como DomesticCat. Las consultas de Hibernate pueden mencionar cualquier clase o interface Java en la cláusula from. La consulta devolverá instancias de todas las clases persistentes que extiendan esa clase o implementen la interface. La siguiente consulta devolvería todos los objetos persistentes. La interface Named podría ser implementada por varias clases persistentes: Nota que estas dos últimas consultas requerirán más de un SELECT SQL. Esto significa que la cláusula order by no ordenará correctamente todo el conjunto resultado. (Significa además que no puedes llamar estas consulta usando Query.scroll().) La cláusula where La cláusula where te permite estrechar la lista de instancias devueltas. Si no existe ningún alias. puedes referirte a las propiedades por nombre: Si existe un alias, usan un nombre cualificado de propiedad: devuelve las instancias de Cat llamadas 'Fritz'. devolverá todas las instancias de Foo para las cuales exista una instancia de bar con una propiedad date igual a la propiedad startDate del Foo. Las expresiones de ruta compuestas hacen la cláusula where extremadamente potente. Considera: Esta consulta se traduce en una consulta SQL con una unión de tabla (interna). Si fueses a escribir algo como terminarías con una consulta que requeriría cuatro uniones de tablas en SQL. El operador = puede ser usado para comparar no sólo propiedades, sino también instancias: La propiedad especial (en minúsculas) id puede ser usada para referenciar el identificador único de un objeto. (También puedes usar su nombre de propiedad.) La segunda consulta es eficiente. ¡No se requiere ninguna unión de tablas! También pueden ser usadas las propiedades de identificadores compuestos. Supón que Person tiene un identificador compuesto consistente en country y medicareNumber. Una vez más, la segunda consulta no requiere ninguna unión de tablas. Asimismo, la propiedad especial class acccede al valor discriminador de una instancia en el caso de persistencia polimórfica. Un nombre de clase Java embebido en la cláusula where será traducido a su valor discriminador. Puedes también especificar propiedades de componentes o tipos compuestos de usuario (y de componentes de componentes, etc). Nunca intentes usar una expresión de ruta que termine en una propiedad de tipo componente (al contrario de una propiedad de un componente). Por ejemplo, si store.owner es una entidad con un componente address Un tipo "any" tiene las propiedades especiales id y class, permiténdonos expresar un join en la siguiente forma (donde AuditLog.item es una propiedad mapeada con <any>). Nota que log.item.class y payment.class harían referencia a los valores de columnas de base de datos completamente diferentes en la consulta anterior. Expresiones Las expresiones permitidas en la cláusula where incluyen la mayoría del tipo de cosas que podrías escribir en SQL: operadores matemáticos +, -, *, / operadores de comparación binarios =, >=, <=, <>, !=, like operadores lógicos and, or, not Paréntesis ( ), indicando agrupación in, not in, between, is null, is not null, is empty, is not empty, member of y not member of Caso "simple", case ... when ... then ... else ... end, y caso "buscado", case when ... then ... else ... end concatenación de cadenas ...||... o concat(...,...) current_date(), current_time(), current_timestamp() second(...), minute(...), hour(...), day(...), month(...), year(...), Cualquier función u operador definido por EJB-QL 3.0: substring(), trim(), lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod() coalesce() y nullif() str() para convertir valores numéricos o temporales a una cadena legible. cast(... as ...), donde el segundo argumento es el nombre de un tipo Hibernate , y extract(... from ...) si cast() y extract() fuesen soportados por la base de datos subyacente. la función index() de HQL, que se aplica a alias de una colección indexada unida. funciones de HQL que tomen expresiones de ruta valuadas en colecciones: size(), minelement(), maxelement(), minindex(), maxindex(), junto a las funciones especiales elements() and indices que pueden ser cuantificadas usando some, all, exists, any, in. Cualquier función escalar SQL soportada por la base de datos como sign(), trunc(), rtrim(), sin() parámetros posicionales JDBC ? parámetros con nombre :name, :start_date, :x1 literales SQL 'foo', 69, 6.66E+2, '1970-01-01 10:00:01.0' constantes Java public static final eg.Color.TABBY in y between pueden usarse como sigue: y pueden escribirse las formas negadas Asimismo, is null y is not null pueden ser usadas para comprobar valores nulos. Los booleanos pueden ser fácilmente usados en expresiones declarando substituciones de consulta HQL en la configuración de Hibernate: true 1, false 0]]> Esto remplazará las palabras clave true y false con los literales 1 y 0 en el SQL traducido de este HQL: Puedes comprobar el tamaño de una colección con la propiedad especial size, o la función especial size(). 0]]> 0]]> Para colecciones indexadas, puedes referirte a los índices máximo y mínimo usando las funciones minindex y maxindex. Similarmente, puedes referirte a los elementos máximo y mínimo de una colección de tipo básico usando las funciones minelement y maxelement. current date]]> 100]]> 10000]]> Las funciones SQL any, some, all, exists, in están soportadas cuando se les pasa el conjunto de elementos o índices de una colección (funciones elements y indices) o el resultado de una subconsulta (ver debajo). all elements(p.scores)]]> Nota que estas construcciones - size, elements, indices, minindex, maxindex, minelement, maxelement - pueden ser usadas solamente en la cláusula where en Hibernate3. Los elementos de colecciones indexadas (arrays, listas, mapas) pueden ser referidos por índice (en una cláusula where solamente): La expresión dentro de [] puede incluso ser una expresión aritmética. HQL provee además el función prefabricada index(), para elementos de una asociación uno-a-muchos o colección de valores. Pueden usarse las funciones SQL escalares soportadas por la base de datos subyacente Si aún no estás convencido de todo esto, piensa cuánto más largo y menos leíble sería la siguiente consulta en SQL: Ayuda: algo como La cláusula order by La lista devuelta por una consulta puede ser ordenada por cualquier propiedad de una clase devuelta o componentes: Los asc o desc opcionales indican ordenamiento ascendente o descendente respectivamente. La cláusula group by Una consulta que devuelve valores agregados puede ser agrupada por cualquier propiedad de una clase devuelta o componentes: Se permite también una cláusula having. Las funciones y funciones de agregación SQL están permitidas en las cláusulas having y order by, si están soportadas por la base de datos subyacente (por ejemplo, no en MySQL). 100 order by count(kitten) asc, sum(kitten.weight) desc]]> Nota que ni la cláusula group by ni la cláusula order by pueden contener expresiones aritméticas. Subconsultas Para bases de datos que soportan subconsultas, Hibernate soporta subconsultas dentro de consultas. Una subconsulta debe ser encerrada entre paréntesis (frecuentemente por una llamada a una función de agregación SQL). Incluso se permiten subconsultas correlacionadas (subconsultas que hacen referencia a un alias en la consulta exterior). ( select avg(cat.weight) from DomesticCat cat )]]> Para las subconsultas con más de una expresión en la lista de selección, puedes usar un constructor de tuplas: Nota que en algunas bases de datos (pero no en Oracle o HSQL), puedes usar constructores de tuplar en otros contextos, por ejemplo al consultar componentes o tipos de usuario compuestos: Que es equivalente a la más verborrágica: Existen dos buenas razones por las cuales podrías no querer hacer este tipo de cosa: primero, no es completamente portable entre plataformas de base de datos; segundo, la consulta ahora es dependiente del orden de propiedades en el documento de mapeo. Ejemplos de HQL Las consultas de Hibernate pueden ser abolutamente potentes y complejas, De hecho, el poder del lenguaje de consulta es uno de los puntos principales de venta de Hibernate. He aquí algunos consultas de ejemplo muy similares a consultas que he usado en un proyecto reciente. ¡Nota que la mayoría de las consultas que escribirás som mucho más simples que estas! La siguiente consulta devuelve el order id, número de items y valor total de la orden para todas las ordenes inpagas de un cliente en particular y valor total mínimo dados, ordenando los resultados por valor total. Al determinar los precios, usa el catálogo actual. La consulta SQL resultante, contra las tablas ORDER, ORDER_LINE, PRODUCT, CATALOG and PRICE tiene cuatro joins interiores y una subselect (no correlacionada). = all ( select cat.effectiveDate from Catalog as cat where cat.effectiveDate < sysdate ) group by order having sum(price.amount) > :minAmount order by sum(price.amount) desc]]> ¡Qué monstruo! Realmente, en la vida real, no estoy muy afilado en subconsultas, de modo que mi consulta fue realmente algo como esto: :minAmount order by sum(price.amount) desc]]> La próxima consulta cuenta el número de pagos en cada estado, excluyendo todos los pagos en el estado AWAITING_APPROVAL donde el estado más reciente fue hecho por el usuario actual. Se traduce en una consulta SQL con dos joins interiores y una subselect correlacionada contra las tablas PAYMENT, PAYMENT_STATUS y PAYMENT_STATUS_CHANGE. PaymentStatus.AWAITING_APPROVAL or ( statusChange.timeStamp = ( select max(change.timeStamp) from PaymentStatusChange change where change.payment = payment ) and statusChange.user <> :currentUser ) group by status.name, status.sortOrder order by status.sortOrder]]> Si hubiese mapeado la colección statusChanges como una lista, en vez de un conjunto, la consulta habría sido mucho más simple de escribir. PaymentStatus.AWAITING_APPROVAL or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser group by status.name, status.sortOrder order by status.sortOrder]]> La próxima consulta usa la función isNull() de MS SQL Server para devolver todas las cuentas y pagos inpagos de la organización a la que pertenece el usuario actual. Se traduce en una consulta SQL con tres joins interiores, un join exterior y una subconsulta contra las tablas ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION y ORG_USER. Para algunas bases de datos, necesitaríamos eliminar la subselect (correlacionada). Sentencias UPDATE y DELETE masivas HQL soporta ahora sentencias UPDATE y DELETE en HQL. Ver para detalles. Consejos y Trucos Puedes contar el número de resultados de una consulta sin devolverlos realmente: Para ordenar un resultado por el tamaño de una colección, usa la siguiente consulta: Si tu base de datos soporta subselects, puedes colocar una condición sobre el tamaño de selección en la cláusula where de tu consulta: = 1]]> Si tu base de datos no soporta subselects, usa la siguiente consulta: = 1]]> Como esta solución no puede devolver un User con cero mensajes debido a la unión interior, la siguiente forma es también útil: Las propiedades de un JavaBean pueden ser ligadas al parámetros de consulta con nombre: Las colecciones son paginables usando la interface Query con un filtro: Los elementos de colección pueden ser ordenados o agrupados usando un filtro de consulta: Puedes hallar el tamaño de una colección sin inicializarla: