hibernate-orm/reference/es/modules/query_hql.xml

1108 lines
46 KiB
XML
Raw Normal View History

<chapter id="queryhql">
<title>HQL: El Lenguaje de Consulta de Hibernate</title>
<para>
Hibernate est&#x00e1; equipado con un lenguaje de consulta extremadamente potente que
(intencionalmente en absoluto) se parece much&#x00ed;simo a SQL. Pero no te enga&#x00f1;es por la sintaxis;
HQL es completamente orientado a objetos, entendiendo nociones como herencia, polimorfismo
y asociaci&#x00f3;n.
</para>
<sect1 id="queryhql-casesensitivity">
<title>Sensibilidad a May&#x00fa;sculas</title>
<para>
Las consultas son insensibles a may&#x00fa;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&#x00fa;sculas. Algunos usuarios encuentran las consultas con
palabras clave en may&#x00fa;sculas m&#x00e1;s le&#x00ed;bles, pero encontramos esta convenci&#x00f3;n fea cuando se encaja
en c&#x00f3;digo Java.
</para>
</sect1>
<sect1 id="queryhql-from">
<title>La cl&#x00e1;usula from</title>
<para>
La consulta m&#x00e1;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&#x00e1; por defecto. De modo que casi siempre escribimos solamente:
</para>
<programlisting><![CDATA[from Cat]]></programlisting>
<para>
La mayor&#x00ed;a del tiempo, necesitar&#x00e1;s asignar un <emphasis>alias</emphasis>, ya que querr&#x00e1;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&#x00ed;amos usar ese alias luego en la consulta. La palabra clave <literal>as</literal>
es opcional; tambi&#x00e9;n podr&#x00ed;amos escribir:
</para>
<programlisting><![CDATA[from Cat cat]]></programlisting>
<para>
Pueden aparecer m&#x00fa;ltiples clases, resultando en un producto cartesiano o uni&#x00f3;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&#x00e1;ctica el nombrar los alias de consulta usando una inicial en min&#x00fa;sculas,
consistente con los est&#x00e1;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&#x00e9;n asignar aliases a entidades asociadas, e incluso a elementos de una colecci&#x00f3;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 &#x00fa;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&#x00f3;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&#x00f3;n, un "fetch" join permite a las asociaciones o colecciones de valores ser inicializadas
junto a sus objetos padres, usando una sola selecci&#x00f3;n. Esto es particularmente &#x00fa;til en el case de una
colecci&#x00f3;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&#x00e1;s
informaci&#x00f3;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&#x00e1;rsele un alias, porque los objetos asociados no deben
ser usados en la cl&#x00e1;usula <literal>where</literal> (ni en cualquier otra cl&#x00e1;usula). Adem&#x00e1;s, los objetos
asociados no son devueltos directamente en los resultados de consulta. En cambio, pueden ser accedidos
v&#x00ed;a el objeto padre. La &#x00fa;nica raz&#x00f3;n por la que necesitar&#x00ed;amos un alias es estamos uniendo recursivamente
otra colecci&#x00f3;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&#x00f3;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&#x00f3;n <literal>with</literal> ad hoc. Es posible crear
un producto cartesiano trayendo por join m&#x00e1;s de una colecci&#x00f3;n en una colecci&#x00f3;n, as&#x00ed; que ten cuidado en
este caso. Traer por join m&#x00fa;ltiples roles de colecci&#x00f3;n tambi&#x00e9;n da a veces resultados inesperados para mapeos
de bag, as&#x00ed; que s&#x00e9; cuidadoso sobre c&#x00f3;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&#x00e1;s usando recuperaci&#x00f3;n perezosa a nivel de propiedad (con instrumentaci&#x00f3;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&#x00e1;usula select</title>
<para>
La cl&#x00e1;usula <literal>select</literal> escoge qu&#x00e9; 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&#x00e1; <literal>mate</literal>s de otros <literal>Cat</literal>s.
Realmente, puedes expresar esta consulta en un forma m&#x00e1;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&#x00fa;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&#x00e1;s &#x00fa;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&#x00f3;n</title>
<para>
Las consultas HQL pueden incluso devolver resultados de funciones de agregaci&#x00f3;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&#x00f3;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&#x00e9;ticos, concatenaci&#x00f3;n, y funciones SQL reconocidas en la cl&#x00e1;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&#x00e1;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&#x00f3;rficas</title>
<para>
Una consulta como:
</para>
<programlisting><![CDATA[from Cat as cat]]></programlisting>
<para>
devuelve instancias no s&#x00f3;lo de <literal>Cat</literal>, sino tambi&#x00e9;n de subclases como
<literal>DomesticCat</literal>. Las consultas de Hibernate pueden mencionar <emphasis>cualquier</emphasis>
clase o interface Java en la cl&#x00e1;usula <literal>from</literal>. La consulta devolver&#x00e1; instancias de todas
las clases persistentes que extiendan esa clase o implementen la interface. La siguiente consulta devolver&#x00ed;a
todos los objetos persistentes.
</para>
<programlisting><![CDATA[from java.lang.Object o]]></programlisting>
<para>
La interface <literal>Named</literal> podr&#x00ed;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 &#x00fa;ltimas consultas requerir&#x00e1;n m&#x00e1;s de un <literal>SELECT</literal> SQL. Esto significa
que la cl&#x00e1;usula <literal>order by</literal> no ordenar&#x00e1; correctamente todo el conjunto resultado.
(Significa adem&#x00e1;s que no puedes llamar estas consulta usando <literal>Query.scroll()</literal>.)
</para>
</sect1>
<sect1 id="queryhql-where">
<title>La cl&#x00e1;usula where</title>
<para>
La cl&#x00e1;usula where te permite estrechar la lista de instancias devueltas. Si no existe ning&#x00fa;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&#x00e1; 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&#x00e1;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&#x00f3;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&#x00ed;as con una consulta que requerir&#x00ed;a cuatro uniones de tablas en SQL.
</para>
<para>
El operador <literal>=</literal> puede ser usado para comparar no s&#x00f3;lo propiedades, sino tambi&#x00e9;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&#x00fa;sculas) <literal>id</literal> puede ser usada para referenciar el identificador
&#x00fa;nico de un objeto. (Tambi&#x00e9;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. &#x00a1;No se requiere ninguna uni&#x00f3;n de tablas!
</para>
<para>
Tambi&#x00e9;n pueden ser usadas las propiedades de identificadores compuestos. Sup&#x00f3;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&#x00e1;s, la segunda consulta no requiere ninguna uni&#x00f3;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&#x00f3;rfica. Un nombre de clase Java embebido en la cl&#x00e1;usula where ser&#x00e1;
traducido a su valor discriminador.
</para>
<programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
<para>
Puedes tambi&#x00e9;n especificar propiedades de componentes o tipos compuestos de usuario (y de componentes
de componentes, etc). Nunca intentes usar una expresi&#x00f3;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&#x00e9;ndonos expresar un join en la siguiente forma (donde <literal>AuditLog.item</literal> es una
propiedad mapeada con <literal>&lt;any&gt;</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&#x00ed;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&#x00e1;usula <literal>where</literal> incluyen la mayor&#x00ed;a del tipo de cosas
que podr&#x00ed;as escribir en SQL:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
operadores matem&#x00e1;ticos <literal>+, -, *, /</literal>
</para>
</listitem>
<listitem>
<para>
operadores de comparaci&#x00f3;n binarios <literal>=, &gt;=, &lt;=, &lt;&gt;, !=, like</literal>
</para>
</listitem>
<listitem>
<para>
operadores l&#x00f3;gicos <literal>and, or, not</literal>
</para>
</listitem>
<listitem>
<para>
Par&#x00e9;ntesis <literal>( )</literal>, indicando agrupaci&#x00f3;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&#x00f3;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&#x00f3;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&#x00e9;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&#x00f3;n <literal>index()</literal> de HQL, que se aplica a alias de una colecci&#x00f3;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&#x00f3;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&#x00e1;metros posicionales JDBC <literal>?</literal>
</para>
</listitem>
<listitem>
<para>
par&#x00e1;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&#x00e1;cilmente usados en expresiones declarando substituciones de consulta HQL
en la configuraci&#x00f3;n de Hibernate:
</para>
<programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
<para>
Esto remplazar&#x00e1; 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&#x00f1;o de una colecci&#x00f3;n con la propiedad especial <literal>size</literal>, o la funci&#x00f3;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 &#x00ed;ndices m&#x00e1;ximo y m&#x00ed;nimo usando las funciones
<literal>minindex</literal> y <literal>maxindex</literal>. Similarmente, puedes referirte a los elementos
m&#x00e1;ximo y m&#x00ed;nimo de una colecci&#x00f3;n de tipo b&#x00e1;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&#x00e1;n soportadas cuando se les pasa
el conjunto de elementos o &#x00ed;ndices de una colecci&#x00f3;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&#x00e1;usula where en Hibernate3.
</para>
<para>
Los elementos de colecciones indexadas (arrays, listas, mapas) pueden ser referidos por &#x00ed;ndice
(en una cl&#x00e1;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&#x00f3;n dentro de <literal>[]</literal> puede incluso ser una expresi&#x00f3;n aritm&#x00e9;tica.
</para>
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item]]></programlisting>
<para>
HQL provee adem&#x00e1;s el funci&#x00f3;n prefabricada <literal>index()</literal>, para elementos de una
asociaci&#x00f3;n uno-a-muchos o colecci&#x00f3;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&#x00fa;n no est&#x00e1;s convencido de todo esto, piensa cu&#x00e1;nto m&#x00e1;s largo y menos le&#x00ed;ble ser&#x00ed;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&#x00e1;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&#x00e1;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&#x00e9;n una cl&#x00e1;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&#x00f3;n SQL est&#x00e1;n permitidas en las cl&#x00e1;usulas
<literal>having</literal> y <literal>order by</literal>, si est&#x00e1;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&#x00e1;usula <literal>group by</literal> ni la cl&#x00e1;usula <literal>order by</literal> pueden
contener expresiones aritm&#x00e9;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&#x00e9;ntesis (frecuentemente por una llamada a una funci&#x00f3;n de agregaci&#x00f3;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&#x00e1;s de una expresi&#x00f3;n en la lista de selecci&#x00f3;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&#x00e1;s verborr&#x00e1;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&#x00ed;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&#x00ed; algunos consultas de ejemplo
muy similares a consultas que he usado en un proyecto reciente. &#x00a1;Nota que la mayor&#x00ed;a de las consultas
que escribir&#x00e1;s som mucho m&#x00e1;s simples que estas!
</para>
<para>
La siguiente consulta devuelve el order id, n&#x00fa;mero de items y valor total de la orden para todas
las ordenes inpagas de un cliente en particular y valor total m&#x00ed;nimo dados, ordenando los resultados
por valor total. Al determinar los precios, usa el cat&#x00e1;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>
&#x00a1;Qu&#x00e9; 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&#x00f3;xima consulta cuenta el n&#x00fa;mero de pagos en cada estado, excluyendo todos los pagos
en el estado <literal>AWAITING_APPROVAL</literal> donde el estado m&#x00e1;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&#x00f3;n <literal>statusChanges</literal> como una lista, en vez de un conjunto,
la consulta habr&#x00ed;a sido mucho m&#x00e1;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&#x00f3;xima consulta usa la funci&#x00f3;n <literal>isNull()</literal> de MS SQL Server para devolver
todas las cuentas y pagos inpagos de la organizaci&#x00f3;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&#x00ed;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&#x00fa;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&#x00f1;o de una colecci&#x00f3;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&#x00f3;n sobre el tama&#x00f1;o de selecci&#x00f3;n
en la cl&#x00e1;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&#x00f3;n no puede devolver un <literal>User</literal> con cero mensajes debido a la uni&#x00f3;n interior,
la siguiente forma es tambi&#x00e9;n &#x00fa;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&#x00e1;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&#x00f3;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&#x00f1;o de una colecci&#x00f3;n sin inicializarla:
</para>
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
</sect1>
</chapter>