432 lines
17 KiB
XML
432 lines
17 KiB
XML
|
<chapter id="querycriteria">
|
||
|
<title>Consultas por Criterios</title>
|
||
|
|
||
|
<para>
|
||
|
Acompañ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á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étodos de fá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ó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á 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á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ó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>
|
||
|
¡Observa que las colecciones de gatitos tenidas por las instancias de <literal>Cat</literal> devueltas
|
||
|
por las dos consultas previas <emphasis>no</emphasis> están prefiltradas por los criterios! Si deseas
|
||
|
recuperar só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ón dinámica de asociaciones</title>
|
||
|
|
||
|
<para>
|
||
|
Puedes especificar la semántica de recuperación de asociaciones en tiempo de ejecució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á tanto <literal>mate</literal> como <literal>kittens</literal> por
|
||
|
unión exterior (outer join). Ver <xref linkend="performance-fetching"/> para más informació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ón, los identificadores y las asociaciones son ignorados. Por defecto,
|
||
|
las propiedades valuadas a nulo son excluídas.
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
Puedes ajustar có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ón y agrupamiento</title>
|
||
|
<para>
|
||
|
La clase <literal>org.hibernate.criterion.Projections</literal> es una fábrica de instancias de
|
||
|
<literal>Projection</literal>. Aplicamos una proyecció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ícito en una consulta por criterios.
|
||
|
Ciertos tipos de proyecciones son definidos para ser <emphasis>proyecciones agrupadas</emphasis>,
|
||
|
que además aparecen en la cláusula SQL <literal>group by</literal>.
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
Puede opcionalmente asignarse un alias a una proyección, de modo que el valor proyectado pueda
|
||
|
ser referido en restricciones u ordenamientos. Aquí 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étodos <literal>alias()</literal> y <literal>as()</literal> simplemente envuelven una instancia
|
||
|
de proyección en otra instancia de <literal>Projection</literal> con alias. Como un atajo, puedes asignar
|
||
|
un alias cuando agregas la proyecció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é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 ámbito de una sesió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én una <literal>DetachedCriteria</literal> puede usarse para expresar una subconsulta.
|
||
|
Las instancias de Criterion implicando subconsultas pueden obtenerse ví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ía de consultas, incluyendo las consultas por criterios, el caché de consulta no es
|
||
|
muy eficiente, debido a que la invalidación del caché de consulta ocurre demasiado frecuentemente.
|
||
|
Sin embargo, hay un tipo especial de consulta donde podemos optimizar el algoritmo de invalidación
|
||
|
de caché: búsquedas por una clave natural constante. En algunas aplicaciones, este tipo de consulta,
|
||
|
ocurre frecuentemente. La API de criterios brinda especial provisión para este caso de uso.
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
Primero, debes mapear la clave natural de tu entidad usando
|
||
|
<literal><natural-id></literal>, y habilitar el uso del caché 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á pensada para uso con entidades con claves naturales
|
||
|
<emphasis>mutable</emphasis>.
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
Seguido, habilita el caché de consulta de Hibernate.
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
Ahora, <literal>Restrictions.naturalId()</literal> nos permite hacer uso de el algoritmo de caché
|
||
|
más eficiente.
|
||
|
</para>
|
||
|
|
||
|
<programlisting><![CDATA[session.createCriteria(User.class)
|
||
|
.add( Restrictions.naturalId()
|
||
|
.set("name", "gavin")
|
||
|
.set("org", "hb")
|
||
|
).setCacheable(true)
|
||
|
.uniqueResult();]]></programlisting>
|
||
|
|
||
|
</sect1>
|
||
|
|
||
|
</chapter>
|