Hibernate.orgCommunity Documentation

Capítulo 17. Consultas por critérios

17.1. Criando uma instância Criteria
17.2. Limitando o conjunto de resultados
17.3. Ordenando resultados
17.4. Associações
17.5. Busca de associação dinâmica
17.6. Exemplos de consultas
17.7. Projeções, agregações e agrupamento.
17.8. Consultas e subconsultas desanexadas.
17.9. Consultas por um identificador natural

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

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

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

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

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

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.

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

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

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

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

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