Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 17. Consultas por criterios

Acompaña a Hibernate una API de consultas por criterios intuitiva y extensible.

## 17.1. Creación de una instancia `Criteria`

La interfaz `org.hibernate.Criteria` representa una consulta contra una clase persistente en particular. La `Session` es una fábrica de instancias de `Criteria`.

    Criteria crit = sess.createCriteria(Cat.class);

    crit.setMaxResults(50);

    List cats = crit.list();

## 17.2. Límitando el conjunto de resultados

Un criterio individual de consulta es una instancia de la interfaz `org.hibernate.criterion.Criterion`. La clase `org.hibernate.criterion.Restrictions` define métodos de fábrica para obtener ciertos tipos incorporados de `Criterion`.

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "Fritz%") )

        .add( Restrictions.between("weight", minWeight, maxWeight) )

        .list();

Las restricciones se pueden agrupar lógicamente.

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "Fritz%") )

        .add( Restrictions.or(

            Restrictions.eq( "age", new Integer(0) ),

            Restrictions.isNull("age")

        ) )

        .list();

    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();

Hay un rango de tipos de criterios incorporados (subclases de `Restrictions`). Uno de los más útiles le permite especificar SQL directamente.

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )

        .list();

El sitio `{alias}` será remplazado por el alias de fila de la entidad consultada.

También puede obtener un criterio de una instancia `Property`. Puede crear una `Property` llamando a `Property.forName()`.

    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();

## 17.3. Orden de los resultados

Puede ordenar los resultados usando `org.hibernate.criterion.Order`.

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "F%")

        .addOrder( Order.asc("name") )

        .addOrder( Order.desc("age") )

        .setMaxResults(50)

        .list();

    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();

## 17.4. Asociaciones

Al navegar asociaciones usando `createCriteria()` puede especificar restricciones en entidades relacionadas:

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "F%") )

        .createCriteria("kittens")

            .add( Restrictions.like("name", "F%") )

        .list();

El segundo `createCriteria()` retorna una nueva instancia de `Criteria`, que se refiere a los elementos de la colección `kittens`.

Hay una alternativa que es útil en ciertas circunstancias:

    List cats = sess.createCriteria(Cat.class)

        .createAlias("kittens", "kt")

        .createAlias("mate", "mt")

        .add( Restrictions.eqProperty("kt.name", "mt.name") )

        .list();

(`createAlias()` no crea una nueva instancia de `Criteria`.)

Las colecciones de gatitos de las instancias `Cat` retornadas por las dos consultas previas no están prefiltradas por los criterios. Si desea recuperar sólo los gatitos que coincidan con los criterios debe usar un `ResultTransformer`.

    List cats = sess.createCriteria(Cat.class)

        .createCriteria("kittens", "kt")

            .add( Restrictions.eq("name", "F%") )

        .setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)

        .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");

    }

Adicionalmente puede manipular el grupo de resultados utilizando una unión externa izquierda:

                    List cats = session.createCriteria( Cat.class )
                           .createAlias("mate", "mt", Criteria.LEFT_JOIN, Restrictions.like("mt.name", "good%") )
                           .addOrder(Order.asc("mt.age"))
                           .list();

Esto retornará todos los `Cat`s -gatos- con una pareja cuyo nombre empiece por "good" ordenado de acuerdo a la edad de la pareja y todos los cats -gatos- que no tengan una pareja. Esto es útil cuando hay necesidad de ordenar o limitar en la base de datos antes de retornar grupos de resultados complejos/grandes y elimina muchas instancias en donde se tendrían que realizar múltiples consultas y unir en memoria los resultados por medio de java.

Sin esta funcionalidad, primero todos los cats sin una pareja tendrían que cargarse en una petición.

Una segunda petición tendría que recuperar los cats -gatos- con las parejas cuyos nombres empiecen por "good" ordenado de acuerdo a la edad de las parejas.

Tercero, en memoria sería necesario unir las listas manualmente.

## 17.5. Recuperación dinámica de asociaciones

Puede especificar la semántica de recuperación de asociaciones en tiempo de ejecución usando `setFetchMode()`.

    List cats = sess.createCriteria(Cat.class)

        .add( Restrictions.like("name", "Fritz%") )

        .setFetchMode("mate", FetchMode.EAGER)

        .setFetchMode("kittens", FetchMode.EAGER)

        .list();

This query will fetch both `mate` and `kittens` by outer join. See Sección 21.1, “Estrategias de recuperación” for more information.

## 17.6. Consultas ejemplo

La clase `org.hibernate.criterion.Example` le permite construir un criterio de consulta a partir de una instancia dada.

    Cat cat = new Cat();

    cat.setSex('F');

    cat.setColor(Color.BLACK);

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .list();

Las propiedades de versión, los identificadores y las asociaciones se ignoran. Por defecto, las propiedades valuadas como nulas se excluyen.

Puede modificar la aplicación del `Example`.

    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();

Puede incluso usar ejemplos para colocar criterios sobre objetos asociados.

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .createCriteria("mate")

            .add( Example.create( cat.getMate() ) )

        .list();

## 17.7. Proyecciones, agregación y agrupamiento

La clase `org.hibernate.criterion.Projections` es una fábrica de instancias de `Projection`. Puede aplicar una proyección a una consulta llamando a `setProjection()`.

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.rowCount() )

        .add( Restrictions.eq("color", Color.BLACK) )

        .list();

    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();

No es necesario ningún "agrupamiento por" explícito en una consulta por criterios. Ciertos tipos de proyecciones son definidos para ser proyecciones agrupadas, que además aparecen en la cláusula SQL `group by`.

Puede asignar 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:

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.alias( Projections.groupProperty("color"), "colr" ) )

        .addOrder( Order.asc("colr") )

        .list();

    List results = session.createCriteria(Cat.class)

        .setProjection( Projections.groupProperty("color").as("colr") )

        .addOrder( Order.asc("colr") )

        .list();

Los métodos `alias()` y `as()` simplemente envuelven una instancia de proyección en otra instancia de `Projection` con alias. Como atajo, puede asignar un alias cuando agregue la proyección a una lista de proyecciones:

    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();

    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();

También puede usar `Property.forName()` para expresar proyecciones:

    List results = session.createCriteria(Cat.class)

        .setProjection( Property.forName("name") )

        .add( Property.forName("color").eq(Color.BLACK) )

        .list();

    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();

## 17.8. Consultas y subconsultas separadas

La clase `DetachedCriteria` le permite crear una consulta fuera del ámbito de una sesión y luego ejecutarla usando una `Session` arbitraria.

    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();

También puede utilizar una `DetachedCriteria` para expresar una subconsulta. Las instancias de Criterion involucrando subconsultas se pueden obtener por medio de `Subqueries` o `Property`.

    DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)

        .setProjection( Property.forName("weight").avg() );

    session.createCriteria(Cat.class)

        .add( Property.forName("weight").gt(avgWeight) )

        .list();

    DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)

        .setProjection( Property.forName("weight") );

    session.createCriteria(Cat.class)

        .add( Subqueries.geAll("weight", weights) )

        .list();

Las subconsultas correlacionadas tambień son posibles:

    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();

## 17.9. Consultas por identificador natural

Para la mayoría de las 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 con demasiada frecuencia. Sin embargo, hay un tipo especial de consulta donde podemos optimizar el algoritmo de invalidación de caché: búsquedas de una clave natural constante. En algunas aplicaciones, este tipo de consulta, ocurre frecuentemente. La API de criterios brinda una provisión especial para este caso.

Primero, mapee la clave natural de su entidad utilizando `<natural-id>` y habilite el uso del caché de segundo nivel.

    <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

    >

Esta funcionalidad no está pensada para uso con entidades con claves naturales mutables.

Una vez que haya habilitado el caché de consulta de Hibernate, `Restrictions.naturalId()` le permite hacer uso del algoritmo de caché más eficiente.

    session.createCriteria(User.class)

        .add( Restrictions.naturalId()

            .set("name", "gavin")

            .set("org", "hb")

        ).setCacheable(true)

        .uniqueResult();

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 16. HQL: El lenguaje de consulta de Hibe...
- Subir
- Inicio
- SiguienteCapítulo 18. SQL Nativo