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

432 lines
17 KiB
XML

<chapter id="querycriteria">
<title>Consultas por Criterios</title>
<para>
Acompa&#x00f1;a a Hibernate una API de consultas por criterios intuitiva y extensible.
</para>
<sect1 id="querycriteria-creating">
<title>Creando una instancia de <literal>Criteria</literal></title>
<para>
La interface <literal>org.hibernate.Criteria</literal> representa una consulta contra
una clase persistente en particular. La <literal>Session</literal> es una f&#x00e1;brica de instancias
de <literal>Criteria</literal>.
</para>
<programlisting><![CDATA[Criteria crit = sess.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-narrowing">
<title>Estrechando el conjunto resultado</title>
<para>
Un criterio individual de consulta es una instancia de la interface
<literal>org.hibernate.criterion.Criterion</literal>. La clase
<literal>org.hibernate.criterion.Restrictions</literal> define m&#x00e9;todos de f&#x00e1;brica para obtener ciertos tipos
prefabricados de <literal>Criterion</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.between("weight", minWeight, maxWeight) )
.list();]]></programlisting>
<para>
Las restricciones pueden ser agrupadas l&#x00f3;gicamente.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.add( Restrictions.or(
Restrictions.eq( "age", new Integer(0) ),
Restrictions.isNull("age")
) )
.list();]]></programlisting>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) )
.add( Restrictions.disjunction()
.add( Restrictions.isNull("age") )
.add( Restrictions.eq("age", new Integer(0) ) )
.add( Restrictions.eq("age", new Integer(1) ) )
.add( Restrictions.eq("age", new Integer(2) ) )
) )
.list();]]></programlisting>
<para>
Hay un gran rango de tipos de criterio prefabricados (subclases de <literal>Restrictions</literal>),
pero uno que es especialmente útil te deja especificar SQL directamente.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
.list();]]></programlisting>
<para>
El sitio <literal>{alias}</literal> ser&#x00e1; remplazado por el alias de fila de la entidad consultada.
</para>
<para>
Un enfoque alternativo para obtener un criterio es tomarlo de una instancia de
<literal>Property</literal>. Puedes crear una <literal>Property</literal> llamando a
<literal>Property.forName()</literal>.
</para>
<programlisting><![CDATA[
Property age = Property.forName("age");
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.disjunction()
.add( age.isNull() )
.add( age.eq( new Integer(0) ) )
.add( age.eq( new Integer(1) ) )
.add( age.eq( new Integer(2) ) )
) )
.add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-ordering">
<title>Ordenando los resultados</title>
<para>
Puedes ordenar los resultados usando <literal>org.hibernate.criterion.Order</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();]]></programlisting>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Property.forName("name").like("F%") )
.addOrder( Property.forName("name").asc() )
.addOrder( Property.forName("age").desc() )
.setMaxResults(50)
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-associations">
<title>Asociaciones</title>
<para>
Puedes especificar f&#x00e1;cilmente restricciones sobre las entidades relacionadas al navegar asociaciones
usando <literal>createCriteria()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.createCriteria("kittens")
.add( Restrictions.like("name", "F%")
.list();]]></programlisting>
<para>
nota que el segundo <literal>createCriteria()</literal> devuelve una nueva instancia de
<literal>Criteria</literal>, que hace referencia a los elementos de la colecci&#x00f3;n
<literal>kittens</literal>.
</para>
<para>
La siguiente forma alternativa es útil en ciertas circunstancias.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.createAlias("kittens", "kt")
.createAlias("mate", "mt")
.add( Restrictions.eqProperty("kt.name", "mt.name") )
.list();]]></programlisting>
<para>
(<literal>createAlias()</literal> no crea una nueva instancia de
<literal>Criteria</literal>.)
</para>
<para>
&#x00a1;Observa que las colecciones de gatitos tenidas por las instancias de <literal>Cat</literal> devueltas
por las dos consultas previas <emphasis>no</emphasis> est&#x00e1;n prefiltradas por los criterios! Si deseas
recuperar s&#x00f3;lo los gatitos que emparejen los criterios, debes usar <literal>returnMaps()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.createCriteria("kittens", "kt")
.add( Restrictions.eq("name", "F%") )
.returnMaps()
.list();
Iterator iter = cats.iterator();
while ( iter.hasNext() ) {
Map map = (Map) iter.next();
Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);
Cat kitten = (Cat) map.get("kt");
}]]></programlisting>
</sect1>
<sect1 id="querycriteria-dynamicfetching" revision="1">
<title>Recuperaci&#x00f3;n din&#x00e1;mica de asociaciones</title>
<para>
Puedes especificar la sem&#x00e1;ntica de recuperaci&#x00f3;n de asociaciones en tiempo de ejecuci&#x00f3;n usando
<literal>setFetchMode()</literal>.
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();]]></programlisting>
<para>
Esta consulta recuperar&#x00e1; tanto <literal>mate</literal> como <literal>kittens</literal> por
uni&#x00f3;n exterior (outer join). Ver <xref linkend="performance-fetching"/> para m&#x00e1;s informaci&#x00f3;n.
</para>
</sect1>
<sect1 id="querycriteria-examples">
<title>Consultas por ejemplos</title>
<para>
La clase <literal>org.hibernate.criterion.Example</literal> te permite construir un criterio de consulta
a partir de una instancia dada.
</para>
<programlisting><![CDATA[Cat cat = new Cat();
cat.setSex('F');
cat.setColor(Color.BLACK);
List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.list();]]></programlisting>
<para>
Las propiedades de versi&#x00f3;n, los identificadores y las asociaciones son ignorados. Por defecto,
las propiedades valuadas a nulo son exclu&#x00ed;das.
</para>
<para>
Puedes ajustar c&#x00f3;mo se aplica el <literal>Example</literal>.
</para>
<programlisting><![CDATA[Example example = Example.create(cat)
.excludeZeroes() //exclude zero valued properties
.excludeProperty("color") //exclude the property named "color"
.ignoreCase() //perform case insensitive string comparisons
.enableLike(); //use like for string comparisons
List results = session.createCriteria(Cat.class)
.add(example)
.list();]]></programlisting>
<para>
Puedes incluso usar ejemplos para colocar criterios sobre objetos asociados.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.add( Example.create(cat) )
.createCriteria("mate")
.add( Example.create( cat.getMate() ) )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-projection">
<title>Proyecciones, agregaci&#x00f3;n y agrupamiento</title>
<para>
La clase <literal>org.hibernate.criterion.Projections</literal> es una f&#x00e1;brica de instancias de
<literal>Projection</literal>. Aplicamos una proyecci&#x00f3;n a una consulta llamando a
<literal>setProjection()</literal>.
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.rowCount() )
.add( Restrictions.eq("color", Color.BLACK) )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount() )
.add( Projections.avg("weight") )
.add( Projections.max("weight") )
.add( Projections.groupProperty("color") )
)
.list();]]></programlisting>
<para>
No es necesario ningún "group by" expl&#x00ed;cito en una consulta por criterios.
Ciertos tipos de proyecciones son definidos para ser <emphasis>proyecciones agrupadas</emphasis>,
que adem&#x00e1;s aparecen en la cl&#x00e1;usula SQL <literal>group by</literal>.
</para>
<para>
Puede opcionalmente asignarse un alias a una proyecci&#x00f3;n, de modo que el valor proyectado pueda
ser referido en restricciones u ordenamientos. Aqu&#x00ed; hay dos formas diferentes de hacer esto:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )
.addOrder( Order.asc("colr") )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.groupProperty("color").as("colr") )
.addOrder( Order.asc("colr") )
.list();]]></programlisting>
<para>
Los m&#x00e9;todos <literal>alias()</literal> y <literal>as()</literal> simplemente envuelven una instancia
de proyecci&#x00f3;n en otra instancia de <literal>Projection</literal> con alias. Como un atajo, puedes asignar
un alias cuando agregas la proyecci&#x00f3;n a una lista de proyecciones:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount(), "catCountByColor" )
.add( Projections.avg("weight"), "avgWeight" )
.add( Projections.max("weight"), "maxWeight" )
.add( Projections.groupProperty("color"), "color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Domestic.class, "cat")
.createAlias("kittens", "kit")
.setProjection( Projections.projectionList()
.add( Projections.property("cat.name"), "catName" )
.add( Projections.property("kit.name"), "kitName" )
)
.addOrder( Order.asc("catName") )
.addOrder( Order.asc("kitName") )
.list();]]></programlisting>
<para>
Puedes tambi&#x00e9;n usar <literal>Property.forName()</literal> para expresar proyecciones:
</para>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Property.forName("name") )
.add( Property.forName("color").eq(Color.BLACK) )
.list();]]></programlisting>
<programlisting><![CDATA[List results = session.createCriteria(Cat.class)
.setProjection( Projections.projectionList()
.add( Projections.rowCount().as("catCountByColor") )
.add( Property.forName("weight").avg().as("avgWeight") )
.add( Property.forName("weight").max().as("maxWeight") )
.add( Property.forName("color").group().as("color" )
)
.addOrder( Order.desc("catCountByColor") )
.addOrder( Order.desc("avgWeight") )
.list();]]></programlisting>
</sect1>
<sect1 id="querycriteria-detachedqueries">
<title>Consultas y subconsultas separadas</title>
<para>
La clase <literal>DetachedCriteria</literal> te deja crear una consulta fuera del &#x00e1;mbito de una sesi&#x00f3;n,
y entonces ejecutarla luego usando alguna <literal>Session</literal> arbitraria.
</para>
<programlisting><![CDATA[DetachedCriteria query = DetachedCriteria.forClass(Cat.class)
.add( Property.forName("sex").eq('F') );
Session session = ....;
Transaction txn = session.beginTransaction();
List results = query.getExecutableCriteria(session).setMaxResults(100).list();
txn.commit();
session.close();]]></programlisting>
<para>
Tambi&#x00e9;n una <literal>DetachedCriteria</literal> puede usarse para expresar una subconsulta.
Las instancias de Criterion implicando subconsultas pueden obtenerse v&#x00ed;a <literal>Subqueries</literal> o
<literal>Property</literal>.
</para>
<programlisting><![CDATA[DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight").avg() );
session.createCriteria(Cat.class)
.add( Property.forName("weight).gt(avgWeight) )
.list();]]></programlisting>
<programlisting><![CDATA[DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)
.setProjection( Property.forName("weight") );
session.createCriteria(Cat.class)
.add( Subqueries.geAll("weight", weights) )
.list();]]></programlisting>
<para>
Incluso son posibles las subconsultas correlacionadas:
</para>
<programlisting><![CDATA[DetachedCriteria avgWeightForSex = DetachedCriteria.forClass(Cat.class, "cat2")
.setProjection( Property.forName("weight").avg() )
.add( Property.forName("cat2.sex").eqProperty("cat.sex") );
session.createCriteria(Cat.class, "cat")
.add( Property.forName("weight).gt(avgWeightForSex) )
.list();]]></programlisting>
</sect1>
<!--TODO: ResultSetTransformer + aliasing. AliasToBeanTransformer allow returning arbitrary
user objects - similar to setResultClass in JDO2. General use of ResultTransformer
could also be explained. -->
<sect1 id="query-criteria-naturalid">
<title>Consultas por identificador natural</title>
<para>
Para la mayor&#x00ed;a de consultas, incluyendo las consultas por criterios, el cach&#x00e9; de consulta no es
muy eficiente, debido a que la invalidaci&#x00f3;n del cach&#x00e9; de consulta ocurre demasiado frecuentemente.
Sin embargo, hay un tipo especial de consulta donde podemos optimizar el algoritmo de invalidaci&#x00f3;n
de cach&#x00e9;: búsquedas por una clave natural constante. En algunas aplicaciones, este tipo de consulta,
ocurre frecuentemente. La API de criterios brinda especial provisi&#x00f3;n para este caso de uso.
</para>
<para>
Primero, debes mapear la clave natural de tu entidad usando
<literal>&lt;natural-id&gt;</literal>, y habilitar el uso del cach&#x00e9; de segundo nivel.
</para>
<programlisting><![CDATA[<class name="User">
<cache usage="read-write"/>
<id name="id">
<generator class="increment"/>
</id>
<natural-id>
<property name="name"/>
<property name="org"/>
</natural-id>
<property name="password"/>
</class>]]></programlisting>
<para>
Nota que esta funcionalidad no est&#x00e1; pensada para uso con entidades con claves naturales
<emphasis>mutable</emphasis>.
</para>
<para>
Seguido, habilita el cach&#x00e9; de consulta de Hibernate.
</para>
<para>
Ahora, <literal>Restrictions.naturalId()</literal> nos permite hacer uso de el algoritmo de cach&#x00e9;
m&#x00e1;s eficiente.
</para>
<programlisting><![CDATA[session.createCriteria(User.class)
.add( Restrictions.naturalId()
.set("name", "gavin")
.set("org", "hb")
).setCacheable(true)
.uniqueResult();]]></programlisting>
</sect1>
</chapter>