Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 17. Consultas por critérios

O Hibernate provê uma API de consulta por critério intuitiva e extensível.

## 17.1. Criando uma instância `Criteria`

A interface `org.hibernate.Criteria` representa a consulta ao invés de uma classe persistente particular. A sessão é uma fábrica para instâncias de `Criteria` .

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

    crit.setMaxResults(50);

    List cats = crit.list();

## 17.2. Limitando o conjunto de resultados

Um critério individual de consulta é uma instância da interface `org.hibernate.criterion.Criterion`. A classe `org.hibernate.criterion.Restrictions` define os métodos da fábrica para obter certos tipos de `Criterion` pré fabricados.

    List cats = sess.createCriteria(Cat.class)

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

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

        .list();

Restrições podem ser logicamente agrupadas.

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

Existe um grande número de critérios pré-fabricados (subclasses de `Restrictions`). Um dos mais úteis permite especificar o SQL diretamente.

    List cats = sess.createCriteria(Cat.class)

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

        .list();

O parâmetro `{alias}` será substituido pelo alias da entidade procurada.

Uma maneira alternativa de obter um critério é apartir de uma instância `Property`. Você pode criar uma `Property` chamando `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. Ordenando resultados

Você poderá ordenar os 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. Associações

Através da navegação de associações usando `createCriteria()`, você pode especificar restrições por entidades relacionadas:

    List cats = sess.createCriteria(Cat.class)

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

        .createCriteria("kittens")

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

        .list();

Note que o segundo `createCriteria()` retorna uma nova instância de `Criteria`, que refere aos elementos da coleção `kittens`.

A seguinte forma alternada é útil em certas circunstâncias:

    List cats = sess.createCriteria(Cat.class)

        .createAlias("kittens", "kt")

        .createAlias("mate", "mt")

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

        .list();

(`createAlias()` não cria uma nova instância de `Criteria`.)

Note que as coleções de kittens mantidas pelas instâncias `Cat`, retornadas pelas duas consultas anteriores não são pré-filtradas pelo critério. Se você desejar recuperar somente os kittens que se encaixarem ao critérios, você deverá usar um `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");

    }

Você pode ainda manipular o conjunto do resultado usando a junção exterior restante:

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

Isto retornará todos os `Cat`s com um mate (amigo) cujo nome inicia com "bom" ordenado pela idade de seu mate e todos os cats que não tem mates. Isto é útil quando houver necessidade de pedir ou limitar a prioridade do banco de dados em retornar conjuntos de resultado complexo/grande e remover muitas instâncias onde consultas múltiplas deveriam ter sido executadas e os resultados unidos pelo java em memória.

Sem este recurso, o primeiro de todos os cats sem um mate teria que ser carregado em uma consulta.

Uma segunda consulta teria que restaurar os cats com os mates cujos os nomes iniciem com "bom" selecionados pelas idades dos mates.

A terceira, em memória; as listas teriam que ser unidas manualmente.

## 17.5. Busca de associação dinâmica

Você deve especificar as semânticas de busca de associação em tempo de execução 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 Seção 21.1, “Estratégias de Busca ” for more information.

## 17.6. Exemplos de consultas

A classe `org.hibernate.criterion.Example` permite que você construa um critério de consulta a partir de uma dada instância.

    Cat cat = new Cat();

    cat.setSex('F');

    cat.setColor(Color.BLACK);

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .list();

Propriedades de versão, identificadores e associações são ignoradas. Por padrão, as propriedades de valor null são excluídas.

Você pode ajustar como o `Exemplo` é aplicado.

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

Você pode até usar os exemplos para colocar os critérios em objetos associados.

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .createCriteria("mate")

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

        .list();

## 17.7. Projeções, agregações e agrupamento.

A classe `org.hibernate.criterion.Projections` é uma fábrica para instâncias de `Projection`. Você pode aplicar uma projeção à uma consulta, chamando o `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();

Não há necessidade de um "agrupamento por" explícito em uma consulta por critério. Certos tipos de projeção são definidos para serem projeções de agrupamento, que também aparecem em uma cláusula `agrupamento por`SQL.

Um alias pode ser atribuído de forma opcional à uma projeção, assim o valor projetado pode ser referenciado em restrições ou ordenações. Aqui seguem duas formas diferentes para fazer isto:

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

Os métodos `alias()` e `as()` simplesmente envolvem uma instância de projeção à outra instância de `Projeção` em alias. Como um atalho, você poderá atribuir um alias quando adicionar a projeção à uma lista de projeção:

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

Você também pode usar um `Property.forName()` para expressar projeções:

    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 e subconsultas desanexadas.

A classe `DetachedCriteria` deixa você criar uma consulta fora do escopo de uma sessão, e depois executá-la usando alguma `Session` arbitrária.

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

Um `DetachedCriteria` também pode ser usado para expressar uma subconsulta. As instâncias de critérios, que envolvem subconsultas, podem ser obtidas através das `Subqueries` ou `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();

Até mesmo as subconsultas correlacionadas são possíveis:

    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 um identificador natural

Para a maioria das consultas, incluindo consultas de critérios, o cache de consulta não é muito eficiente, pois a invalidação do cache de consulta ocorre com muita frequência. No entanto, não há um tipo de consulta especial onde possamos otimizar um algoritmo de invalidação de cache: consultas realizadas por chaves naturais constantes. Em algumas aplicações, este tipo de consulta ocorre com freqüência. O API de critério provê provisão especial para este caso de uso.

Primeiro, você deve mapear a chave natural de sua entidade usando um `<natural-id>` e habilitar o uso de um cache de segundo nível.

    <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

    >

Note que esta funcionalidade não é proposta para o uso com entidades com chaves naturais mutáveis.

Uma vez que você tenha ativado o cache de consulta Hibernate, o `Restrictions.naturalId()` nos permite que utilizemos um algoritmo de cache mais 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: A Linguagem de Consultas do Hib...
- Acima
- Principal
- PróximaCapítulo 18. SQL Nativo