Hibernate.orgCommunity Documentation

Capítulo 16. Consultas por criterios

16.1. Creación de una instancia Criteria
16.2. Límitando el conjunto de resultados
16.3. Orden de los resultados
16.4. Asociaciones
16.5. Recuperación dinámica de asociaciones
16.6. Consultas ejemplo
16.7. Proyecciones, agregación y agrupamiento
16.8. Consultas y subconsultas separadas
16.9. Consultas por identificador natural

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

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

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

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

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 Cats -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.

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 20.1, “Estrategias de recuperación” for more information.

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

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

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

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