1108 lines
46 KiB
XML
1108 lines
46 KiB
XML
<chapter id="queryhql">
|
|
<title>HQL: El Lenguaje de Consulta de Hibernate</title>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<sect1 id="queryhql-casesensitivity">
|
|
<title>Sensibilidad a Mayúsculas</title>
|
|
|
|
<para>
|
|
Las consultas son insensibles a mayúsculas, excepto para nombres de clases Java y propiedades. De modo que
|
|
<literal>SeLeCT</literal> es lo mismo que <literal>sELEct</literal> e igual a <literal>SELECT</literal>,
|
|
pero <literal>org.hibernate.eg.FOO</literal> no lo es a <literal>org.hibernate.eg.Foo</literal> y
|
|
<literal>foo.barSet</literal> no es igual a <literal>foo.BARSET</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-from">
|
|
<title>La cláusula from</title>
|
|
|
|
<para>
|
|
La consulta más simple posible de Hibernate es de la forma:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from eg.Cat]]></programlisting>
|
|
|
|
<para>
|
|
que simplemente devuelve todas las instancias de la clase <literal>eg.Cat</literal>.
|
|
Usualmente no necesitamos cualificar el nombre de la clase, ya que <literal>auto-import</literal>
|
|
está por defecto. De modo que casi siempre escribimos solamente:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat]]></programlisting>
|
|
|
|
<para>
|
|
La mayoría del tiempo, necesitarás asignar un <emphasis>alias</emphasis>, ya que querrás referirte al
|
|
<literal>Cat</literal> en otras partes de la consulta.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat]]></programlisting>
|
|
|
|
<para>
|
|
Esta consulta asigna el alias <literal>cat</literal> a las instancias de <literal>Cat</literal>,
|
|
de modo que podríamos usar ese alias luego en la consulta. La palabra clave <literal>as</literal>
|
|
es opcional; también podríamos escribir:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat cat]]></programlisting>
|
|
|
|
<para>
|
|
Pueden aparecer múltiples clases, resultando en un producto cartesiano o unión "cruzada" (cross join).
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Formula, Parameter]]></programlisting>
|
|
<programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
|
|
|
|
<para>
|
|
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, <literal>domesticCat</literal>).
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-joins" revision="1">
|
|
<title>Asociaciones y uniones (joins)</title>
|
|
|
|
<para>
|
|
Podemos también asignar aliases a entidades asociadas, e incluso a elementos de una colección de valores,
|
|
usando una <literal>join</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat
|
|
inner join cat.mate as mate
|
|
left outer join cat.kittens as kitten]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
|
|
|
|
<para>
|
|
Los tipos de join soportados son prestados de ANSI SQL
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
<literal>inner join</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>left outer join</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>right outer join</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>full join</literal> (no útil usualmente)
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
Las construcciones <literal>inner join</literal>, <literal>left outer join</literal> y
|
|
<literal>right outer join</literal> pueden ser abreviadas.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat
|
|
join cat.mate as mate
|
|
left join cat.kittens as kitten]]></programlisting>
|
|
|
|
<para>
|
|
Puedes proveer condiciones de unión extra usando la palabra clave <literal>with</literal> de HQL.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat
|
|
left join cat.kittens as kitten
|
|
with kitten.bodyWeight > 10.0]]></programlisting>
|
|
|
|
<para>
|
|
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 <xref linkend="performance-fetching"/> para más
|
|
información.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat
|
|
inner join fetch cat.mate
|
|
left join fetch cat.kittens]]></programlisting>
|
|
|
|
<para>
|
|
Usualmente a un fetch join no se necesita asignársele un alias, porque los objetos asociados no deben
|
|
ser usados en la cláusula <literal>where</literal> (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:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat
|
|
inner join fetch cat.mate
|
|
left join fetch cat.kittens child
|
|
left join fetch child.kittens]]></programlisting>
|
|
|
|
<para>
|
|
Nota que la construcción <literal>fetch</literal> no puede usarse en consultas llamadas usando
|
|
<literal>scroll()</literal> o <literal>iterate()</literal>. Ni debe usarse <literal>fetch</literal>
|
|
junto con <literal>setMaxResults()</literal> o <literal>setFirstResult()</literal>. Tampoco puede usarse
|
|
<literal>fetch</literal> junto a una condición <literal>with</literal> 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
|
|
<literal>full join fetch</literal> y <literal>right join fetch</literal> no son significativos.
|
|
</para>
|
|
|
|
<para>
|
|
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
|
|
<literal>fetch all properties</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Document fetch all properties order by name]]></programlisting>
|
|
<programlisting><![CDATA[from Document doc fetch all properties where lower(doc.name) like '%cats%']]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-select">
|
|
<title>La cláusula select</title>
|
|
|
|
<para>
|
|
La cláusula <literal>select</literal> escoge qué objetos y propiedades devolver in el conjunto resultado
|
|
de la consulta. Considera:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select mate
|
|
from Cat as cat
|
|
inner join cat.mate as mate]]></programlisting>
|
|
|
|
<para>
|
|
La consulta seleccionará <literal>mate</literal>s de otros <literal>Cat</literal>s.
|
|
Realmente, puedes expresar esta consulta en un forma más compacta como:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
|
|
|
|
<para>
|
|
Las consultas pueden devolver propiedades de cualquier tipo de valor incluyendo propiedades de
|
|
tipo componente:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cat.name from DomesticCat cat
|
|
where cat.name like 'fri%']]></programlisting>
|
|
|
|
<programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
|
|
|
|
<para>
|
|
Las consultas pueden devolver múltiples objetos y/o propiedades como un array de tipo
|
|
<literal>Object[]</literal>,
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select mother, offspr, mate.name
|
|
from DomesticCat as mother
|
|
inner join mother.mate as mate
|
|
left outer join mother.kittens as offspr]]></programlisting>
|
|
|
|
<para>
|
|
o como una <literal>List</literal>,
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select new list(mother, offspr, mate.name)
|
|
from DomesticCat as mother
|
|
inner join mother.mate as mate
|
|
left outer join mother.kittens as offspr]]></programlisting>
|
|
|
|
<para>
|
|
o como un objeto real Java de tipo seguro,
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select new Family(mother, mate, offspr)
|
|
from DomesticCat as mother
|
|
join mother.mate as mate
|
|
left join mother.kittens as offspr]]></programlisting>
|
|
|
|
<para>
|
|
asumiendo que la clase <literal>Family</literal> tiene un constructor apropiado.
|
|
</para>
|
|
|
|
<para>
|
|
Puedes asignar aliases para seleccionar expresiones usando <literal>as</literal>:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
|
|
from Cat cat]]></programlisting>
|
|
|
|
<para>
|
|
Esto es lo más útil cuando se usa junto con <literal>select new map</literal>:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
|
|
from Cat cat]]></programlisting>
|
|
|
|
<para>
|
|
Esta consulta devuelve un <literal>Map</literal> de aliases a valores seleccionados.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-aggregation">
|
|
<title>Funciones de agregación</title>
|
|
|
|
<para>
|
|
Las consultas HQL pueden incluso devolver resultados de funciones de agregación sobre propiedades:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
|
|
from Cat cat]]></programlisting>
|
|
|
|
<!-- NO LONGER SUPPORTED
|
|
<para>
|
|
Collections may also appear inside aggregate functions in the <literal>select</literal>
|
|
clause.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cat, count( elements(cat.kittens) )
|
|
from Cat cat group by cat]]></programlisting>
|
|
-->
|
|
|
|
<para>
|
|
Las funciones de agregación soportadas son
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
<literal>avg(...), sum(...), min(...), max(...)</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>count(*)</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>count(...), count(distinct ...), count(all...)</literal>
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
Puedes usar operadores aritméticos, concatenación, y funciones SQL reconocidas en la cláusula select:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cat.weight + sum(kitten.weight)
|
|
from Cat cat
|
|
join cat.kittens kitten
|
|
group by cat.id, cat.weight]]></programlisting>
|
|
|
|
<programlisting><![CDATA[select firstName||' '||initial||' '||upper(lastName) from Person]]></programlisting>
|
|
|
|
<para>
|
|
Las palabras clave <literal>distinct</literal> y <literal>all</literal> pueden ser usadas y tienen las misma
|
|
semántica que en SQL.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select distinct cat.name from Cat cat
|
|
|
|
select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-polymorphism">
|
|
<title>Consultas polimórficas</title>
|
|
|
|
<para>
|
|
Una consulta como:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat]]></programlisting>
|
|
|
|
<para>
|
|
devuelve instancias no sólo de <literal>Cat</literal>, sino también de subclases como
|
|
<literal>DomesticCat</literal>. Las consultas de Hibernate pueden mencionar <emphasis>cualquier</emphasis>
|
|
clase o interface Java en la cláusula <literal>from</literal>. 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.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from java.lang.Object o]]></programlisting>
|
|
|
|
<para>
|
|
La interface <literal>Named</literal> podría ser implementada por varias clases persistentes:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
|
|
|
|
<para>
|
|
Nota que estas dos últimas consultas requerirán más de un <literal>SELECT</literal> SQL. Esto significa
|
|
que la cláusula <literal>order by</literal> no ordenará correctamente todo el conjunto resultado.
|
|
(Significa además que no puedes llamar estas consulta usando <literal>Query.scroll()</literal>.)
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-where">
|
|
<title>La cláusula where</title>
|
|
|
|
<para>
|
|
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:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
|
|
|
|
<para>
|
|
Si existe un alias, usan un nombre cualificado de propiedad:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
|
|
|
|
<para>
|
|
devuelve las instancias de <literal>Cat</literal> llamadas 'Fritz'.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select foo
|
|
from Foo foo, Bar bar
|
|
where foo.startDate = bar.date]]></programlisting>
|
|
|
|
<para>
|
|
devolverá todas las instancias de <literal>Foo</literal> para las cuales exista una instancia
|
|
de <literal>bar</literal> con una propiedad <literal>date</literal> igual a la propiedad
|
|
<literal>startDate</literal> del <literal>Foo</literal>. Las expresiones de ruta compuestas hacen
|
|
la cláusula <literal>where</literal> extremadamente potente. Considera:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
|
|
|
|
<para>
|
|
Esta consulta se traduce en una consulta SQL con una unión de tabla (interna). Si fueses a escribir algo como
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Foo foo
|
|
where foo.bar.baz.customer.address.city is not null]]></programlisting>
|
|
|
|
<para>
|
|
terminarías con una consulta que requeriría cuatro uniones de tablas en SQL.
|
|
</para>
|
|
|
|
<para>
|
|
El operador <literal>=</literal> puede ser usado para comparar no sólo propiedades, sino también instancias:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
|
|
|
|
<programlisting><![CDATA[select cat, mate
|
|
from Cat cat, Cat mate
|
|
where cat.mate = mate]]></programlisting>
|
|
|
|
<para>
|
|
La propiedad especial (en minúsculas) <literal>id</literal> puede ser usada para referenciar el identificador
|
|
único de un objeto. (También puedes usar su nombre de propiedad.)
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat where cat.id = 123
|
|
|
|
from Cat as cat where cat.mate.id = 69]]></programlisting>
|
|
|
|
<para>
|
|
La segunda consulta es eficiente. ¡No se requiere ninguna unión de tablas!
|
|
</para>
|
|
|
|
<para>
|
|
También pueden ser usadas las propiedades de identificadores compuestos. Supón que <literal>Person</literal>
|
|
tiene un identificador compuesto consistente en <literal>country</literal> y <literal>medicareNumber</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from bank.Person person
|
|
where person.id.country = 'AU'
|
|
and person.id.medicareNumber = 123456]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from bank.Account account
|
|
where account.owner.id.country = 'AU'
|
|
and account.owner.id.medicareNumber = 123456]]></programlisting>
|
|
|
|
<para>
|
|
Una vez más, la segunda consulta no requiere ninguna unión de tablas.
|
|
</para>
|
|
|
|
<para>
|
|
Asimismo, la propiedad especial <literal>class</literal> 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.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
|
|
|
|
<para>
|
|
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 <literal>store.owner</literal>
|
|
es una entidad con un componente <literal>address</literal>
|
|
</para>
|
|
|
|
<programlisting><![CDATA[store.owner.address.city // okay
|
|
store.owner.address // error!]]></programlisting>
|
|
|
|
<para>
|
|
Un tipo "any" tiene las propiedades especiales <literal>id</literal> y <literal>class</literal>,
|
|
permiténdonos expresar un join en la siguiente forma (donde <literal>AuditLog.item</literal> es una
|
|
propiedad mapeada con <literal><any></literal>).
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from AuditLog log, Payment payment
|
|
where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
|
|
|
|
<para>
|
|
Nota que <literal>log.item.class</literal> y <literal>payment.class</literal> harían referencia a
|
|
los valores de columnas de base de datos completamente diferentes en la consulta anterior.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-expressions">
|
|
<title>Expresiones</title>
|
|
|
|
<para>
|
|
Las expresiones permitidas en la cláusula <literal>where</literal> incluyen la mayoría del tipo de cosas
|
|
que podrías escribir en SQL:
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
operadores matemáticos <literal>+, -, *, /</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
operadores de comparación binarios <literal>=, >=, <=, <>, !=, like</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
operadores lógicos <literal>and, or, not</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Paréntesis <literal>( )</literal>, indicando agrupación
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>in</literal>,
|
|
<literal>not in</literal>,
|
|
<literal>between</literal>,
|
|
<literal>is null</literal>,
|
|
<literal>is not null</literal>,
|
|
<literal>is empty</literal>,
|
|
<literal>is not empty</literal>,
|
|
<literal>member of</literal> y
|
|
<literal>not member of</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Caso "simple", <literal>case ... when ... then ... else ... end</literal>,
|
|
y caso "buscado", <literal>case when ... then ... else ... end</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
concatenación de cadenas <literal>...||...</literal> o <literal>concat(...,...)</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>current_date()</literal>, <literal>current_time()</literal>,
|
|
<literal>current_timestamp()</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>second(...)</literal>, <literal>minute(...)</literal>,
|
|
<literal>hour(...)</literal>, <literal>day(...)</literal>,
|
|
<literal>month(...)</literal>, <literal>year(...)</literal>,
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
Cualquier función u operador definido por EJB-QL 3.0: <literal>substring(), trim(),
|
|
lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>coalesce()</literal> y <literal>nullif()</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>str()</literal> para convertir valores numéricos o temporales a una cadena legible.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>cast(... as ...)</literal>, donde el segundo argumento es el nombre de un tipo Hibernate
|
|
, y <literal>extract(... from ...)</literal> si <literal>cast()</literal> y
|
|
<literal>extract()</literal> fuesen soportados por la base de datos subyacente.
|
|
</para>
|
|
</listitem>
|
|
|
|
|
|
<listitem>
|
|
<para>
|
|
la función <literal>index()</literal> de HQL, que se aplica a alias de una colección
|
|
indexada unida.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
funciones de HQL que tomen expresiones de ruta valuadas en colecciones: <literal>size(),
|
|
minelement(), maxelement(), minindex(), maxindex()</literal>, junto a las funciones especiales
|
|
<literal>elements()</literal> and <literal>indices</literal> que pueden ser cuantificadas usando
|
|
<literal>some, all, exists, any, in</literal>.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
Cualquier función escalar SQL soportada por la base de datos como <literal>sign()</literal>,
|
|
<literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
parámetros posicionales JDBC <literal>?</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
parámetros con nombre <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
literales SQL <literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
|
|
<literal>'1970-01-01 10:00:01.0'</literal>
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
constantes Java <literal>public static final</literal> <literal>eg.Color.TABBY</literal>
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
<literal>in</literal> y <literal>between</literal> pueden usarse como sigue:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
|
|
|
|
<programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
|
|
|
|
<para>
|
|
y pueden escribirse las formas negadas
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
|
|
|
|
<programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
|
|
|
|
<para>
|
|
Asimismo, <literal>is null</literal> y <literal>is not null</literal> pueden ser usadas para comprobar
|
|
valores nulos.
|
|
</para>
|
|
|
|
<para>
|
|
Los booleanos pueden ser fácilmente usados en expresiones declarando substituciones de consulta HQL
|
|
en la configuración de Hibernate:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
|
|
|
|
<para>
|
|
Esto remplazará las palabras clave <literal>true</literal> y <literal>false</literal> con los literales
|
|
<literal>1</literal> y <literal>0</literal> en el SQL traducido de este HQL:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
|
|
|
|
<para>
|
|
Puedes comprobar el tamaño de una colección con la propiedad especial <literal>size</literal>, o la función
|
|
especial <literal>size()</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
|
|
|
|
<para>
|
|
Para colecciones indexadas, puedes referirte a los índices máximo y mínimo usando las funciones
|
|
<literal>minindex</literal> y <literal>maxindex</literal>. Similarmente, puedes referirte a los elementos
|
|
máximo y mínimo de una colección de tipo básico usando las funciones
|
|
<literal>minelement</literal> y <literal>maxelement</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Calendar cal where maxelement(cal.holidays) > current date]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Order order where maxindex(order.items) > 100]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Order order where minelement(order.items) > 10000]]></programlisting>
|
|
|
|
<para>
|
|
Las funciones SQL <literal>any, some, all, exists, in</literal> están soportadas cuando se les pasa
|
|
el conjunto de elementos o índices de una colección (funciones <literal>elements</literal> y
|
|
<literal>indices</literal>) o el resultado de una subconsulta (ver debajo).
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select mother from Cat as mother, Cat as kit
|
|
where kit in elements(foo.kittens)]]></programlisting>
|
|
|
|
<programlisting><![CDATA[select p from NameList list, Person p
|
|
where p.name = some elements(list.names)]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
|
|
|
|
<para>
|
|
Nota que estas construcciones - <literal>size</literal>, <literal>elements</literal>,
|
|
<literal>indices</literal>, <literal>minindex</literal>, <literal>maxindex</literal>,
|
|
<literal>minelement</literal>, <literal>maxelement</literal> - pueden ser usadas solamente
|
|
en la cláusula where en Hibernate3.
|
|
</para>
|
|
|
|
<para>
|
|
Los elementos de colecciones indexadas (arrays, listas, mapas) pueden ser referidos por índice
|
|
(en una cláusula where solamente):
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
|
|
|
|
<programlisting><![CDATA[select person from Person person, Calendar calendar
|
|
where calendar.holidays['national day'] = person.birthDay
|
|
and person.nationality.calendar = calendar]]></programlisting>
|
|
|
|
<programlisting><![CDATA[select item from Item item, Order order
|
|
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
|
|
|
|
<programlisting><![CDATA[select item from Item item, Order order
|
|
where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
|
|
|
|
<para>
|
|
La expresión dentro de <literal>[]</literal> puede incluso ser una expresión aritmética.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select item from Item item, Order order
|
|
where order.items[ size(order.items) - 1 ] = item]]></programlisting>
|
|
|
|
<para>
|
|
HQL provee además el función prefabricada <literal>index()</literal>, para elementos de una
|
|
asociación uno-a-muchos o colección de valores.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select item, index(item) from Order order
|
|
join order.items item
|
|
where index(item) < 5]]></programlisting>
|
|
|
|
<para>
|
|
Pueden usarse las funciones SQL escalares soportadas por la base de datos subyacente
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
|
|
|
|
<para>
|
|
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:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cust
|
|
from Product prod,
|
|
Store store
|
|
inner join store.customers cust
|
|
where prod.name = 'widget'
|
|
and store.location.name in ( 'Melbourne', 'Sydney' )
|
|
and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
|
|
|
|
<para>
|
|
<emphasis>Ayuda:</emphasis> algo como
|
|
</para>
|
|
|
|
<programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
|
|
FROM customers cust,
|
|
stores store,
|
|
locations loc,
|
|
store_customers sc,
|
|
product prod
|
|
WHERE prod.name = 'widget'
|
|
AND store.loc_id = loc.id
|
|
AND loc.name IN ( 'Melbourne', 'Sydney' )
|
|
AND sc.store_id = store.id
|
|
AND sc.cust_id = cust.id
|
|
AND prod.id = ALL(
|
|
SELECT item.prod_id
|
|
FROM line_items item, orders o
|
|
WHERE item.order_id = o.id
|
|
AND cust.current_order = o.id
|
|
)]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-ordering">
|
|
<title>La cláusula order by</title>
|
|
|
|
<para>
|
|
La lista devuelta por una consulta puede ser ordenada por cualquier propiedad de una clase devuelta
|
|
o componentes:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from DomesticCat cat
|
|
order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
|
|
|
|
<para>
|
|
Los <literal>asc</literal> o <literal>desc</literal> opcionales indican ordenamiento ascendente o
|
|
descendente respectivamente.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-grouping">
|
|
<title>La cláusula group by</title>
|
|
|
|
<para>
|
|
Una consulta que devuelve valores agregados puede ser agrupada por cualquier propiedad de una clase
|
|
devuelta o componentes:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
|
|
from Cat cat
|
|
group by cat.color]]></programlisting>
|
|
|
|
<programlisting><![CDATA[select foo.id, avg(name), max(name)
|
|
from Foo foo join foo.names name
|
|
group by foo.id]]></programlisting>
|
|
|
|
<para>
|
|
Se permite también una cláusula <literal>having</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
|
|
from Cat cat
|
|
group by cat.color
|
|
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
|
|
|
|
<para>
|
|
Las funciones y funciones de agregación SQL están permitidas en las cláusulas
|
|
<literal>having</literal> y <literal>order by</literal>, si están soportadas por la base de datos
|
|
subyacente (por ejemplo, no en MySQL).
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select cat
|
|
from Cat cat
|
|
join cat.kittens kitten
|
|
group by cat
|
|
having avg(kitten.weight) > 100
|
|
order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
|
|
|
|
<para>
|
|
Nota que ni la cláusula <literal>group by</literal> ni la cláusula <literal>order by</literal> pueden
|
|
contener expresiones aritméticas.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-subqueries">
|
|
<title>Subconsultas</title>
|
|
|
|
<para>
|
|
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).
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as fatcat
|
|
where fatcat.weight > (
|
|
select avg(cat.weight) from DomesticCat cat
|
|
)]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from DomesticCat as cat
|
|
where cat.name = some (
|
|
select name.nickName from Name as name
|
|
)]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from Cat as cat
|
|
where not exists (
|
|
from Cat as mate where mate.mate = cat
|
|
)]]></programlisting>
|
|
|
|
<programlisting><![CDATA[from DomesticCat as cat
|
|
where cat.name not in (
|
|
select name.nickName from Name as name
|
|
)]]></programlisting>
|
|
|
|
<para>
|
|
Para las subconsultas con más de una expresión en la lista de selección, puedes usar un constructor
|
|
de tuplas:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Cat as cat
|
|
where not ( cat.name, cat.color ) in (
|
|
select cat.name, cat.color from DomesticCat cat
|
|
)]]></programlisting>
|
|
|
|
<para>
|
|
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:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
|
|
|
|
<para>
|
|
Que es equivalente a la más verborrágica:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-examples">
|
|
<title>Ejemplos de HQL</title>
|
|
|
|
<para>
|
|
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!
|
|
</para>
|
|
|
|
<para>
|
|
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 <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
|
|
<literal>CATALOG</literal> and <literal>PRICE</literal> tiene cuatro joins interiores y una subselect
|
|
(no correlacionada).
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
|
|
from Order as order
|
|
join order.lineItems as item
|
|
join item.product as product,
|
|
Catalog as catalog
|
|
join catalog.prices as price
|
|
where order.paid = false
|
|
and order.customer = :customer
|
|
and price.product = product
|
|
and catalog.effectiveDate < sysdate
|
|
and catalog.effectiveDate >= 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]]></programlisting>
|
|
|
|
<para>
|
|
¡Qué monstruo! Realmente, en la vida real, no estoy muy afilado en subconsultas, de modo que mi
|
|
consulta fue realmente algo como esto:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
|
|
from Order as order
|
|
join order.lineItems as item
|
|
join item.product as product,
|
|
Catalog as catalog
|
|
join catalog.prices as price
|
|
where order.paid = false
|
|
and order.customer = :customer
|
|
and price.product = product
|
|
and catalog = :currentCatalog
|
|
group by order
|
|
having sum(price.amount) > :minAmount
|
|
order by sum(price.amount) desc]]></programlisting>
|
|
|
|
<para>
|
|
La próxima consulta cuenta el número de pagos en cada estado, excluyendo todos los pagos
|
|
en el estado <literal>AWAITING_APPROVAL</literal> 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 <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal> y
|
|
<literal>PAYMENT_STATUS_CHANGE</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select count(payment), status.name
|
|
from Payment as payment
|
|
join payment.currentStatus as status
|
|
join payment.statusChanges as statusChange
|
|
where payment.status.name <> 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]]></programlisting>
|
|
|
|
<para>
|
|
Si hubiese mapeado la colección <literal>statusChanges</literal> como una lista, en vez de un conjunto,
|
|
la consulta habría sido mucho más simple de escribir.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select count(payment), status.name
|
|
from Payment as payment
|
|
join payment.currentStatus as status
|
|
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
|
|
or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
|
|
group by status.name, status.sortOrder
|
|
order by status.sortOrder]]></programlisting>
|
|
|
|
<para>
|
|
La próxima consulta usa la función <literal>isNull()</literal> 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 <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
|
|
<literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> y <literal>ORG_USER</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select account, payment
|
|
from Account as account
|
|
left outer join account.payments as payment
|
|
where :currentUser in elements(account.holder.users)
|
|
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
|
|
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
|
|
|
|
<para>
|
|
Para algunas bases de datos, necesitaríamos eliminar la subselect (correlacionada).
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select account, payment
|
|
from Account as account
|
|
join account.holder.users as user
|
|
left outer join account.payments as payment
|
|
where :currentUser = user
|
|
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
|
|
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-bulk">
|
|
<title>Sentencias UPDATE y DELETE masivas</title>
|
|
|
|
<para>
|
|
HQL soporta ahora sentencias UPDATE y DELETE en HQL.
|
|
Ver <xref linkend="batch-direct"/> para detalles.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="queryhql-tipstricks">
|
|
<title>Consejos y Trucos</title>
|
|
|
|
<para>
|
|
Puedes contar el número de resultados de una consulta sin devolverlos realmente:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
|
|
|
|
<para>
|
|
Para ordenar un resultado por el tamaño de una colección, usa la siguiente consulta:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select usr.id, usr.name
|
|
from User as usr
|
|
left join usr.messages as msg
|
|
group by usr.id, usr.name
|
|
order by count(msg)]]></programlisting>
|
|
|
|
<para>
|
|
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:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
|
|
|
|
<para>
|
|
Si tu base de datos no soporta subselects, usa la siguiente consulta:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select usr.id, usr.name
|
|
from User usr.name
|
|
join usr.messages msg
|
|
group by usr.id, usr.name
|
|
having count(msg) >= 1]]></programlisting>
|
|
|
|
<para>
|
|
Como esta solución no puede devolver un <literal>User</literal> con cero mensajes debido a la unión interior,
|
|
la siguiente forma es también útil:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[select usr.id, usr.name
|
|
from User as usr
|
|
left join usr.messages as msg
|
|
group by usr.id, usr.name
|
|
having count(msg) = 0]]></programlisting>
|
|
|
|
<para>
|
|
Las propiedades de un JavaBean pueden ser ligadas al parámetros de consulta con nombre:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
|
|
q.setProperties(fooBean); // fooBean has getName() and getSize()
|
|
List foos = q.list();]]></programlisting>
|
|
|
|
<para>
|
|
Las colecciones son paginables usando la interface <literal>Query</literal> con un filtro:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
|
|
q.setMaxResults(PAGE_SIZE);
|
|
q.setFirstResult(PAGE_SIZE * pageNumber);
|
|
List page = q.list();]]></programlisting>
|
|
|
|
<para>
|
|
Los elementos de colección pueden ser ordenados o agrupados usando un filtro de consulta:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[Collection orderedCollection = s.filter( collection, "order by this.amount" );
|
|
Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );]]></programlisting>
|
|
|
|
<para>
|
|
Puedes hallar el tamaño de una colección sin inicializarla:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|