Hibernate.org Community Documentation

- Précédent
- Suivant

## Chapitre 17. Requêtes par critères

Hibernate offre une API d'interrogation par critères intuitive et extensible.

## 17.1. Créer une instance de `Criteria`

L'interface `net.sf.hibernate.Criteria` représente une requête sur une classe persistante donnée. La `Session` fournit les instances de `Criteria`.

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

    crit.setMaxResults(50);

    List cats = crit.list();

## 17.2. Restriction du résultat

Un critère de recherche (criterion) individuel est une instance de l'interface `org.hibernate.criterion.Criterion`. La classe `org.hibernate.criterion.Restrictions` définit des méthodes de fabrique pour obtenir des types de `Criterion` intégrés.

    List cats = sess.createCriteria(Cat.class)

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

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

        .list();

Les restrictions peuvent être groupées de manière logique.

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

Il y a un grand choix de types de critères intégrés (sous classes de `Restriction`), dont un est particulièrement utile puisqu'il vous permet de spécifier directement SQL.

    List cats = sess.createCriteria(Cat.class)

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

        .list();

La zone `{alias}` sera remplacée par l'alias de colonne de l'entité que l'on souhaite interroger.

Une autre approche pour obtenir un critère est de le récupérer d'une instance de `Property`. Vous pouvez créer une `Property` en appelant `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. Trier les résultats

Vous pouvez trier les résultats en utilisant `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. Associations

En naviguant les associations qui utilisent `createCriteria()`, vous pouvez spécifier des contraintes associées à des entités :

    List cats = sess.createCriteria(Cat.class)

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

        .createCriteria("kittens")

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

        .list();

Notez que la seconde `createCriteria()` retourne une nouvelle instance de `Criteria`, qui se rapporte aux éléments de la collection `kittens`.

La forme alternative suivante est utile dans certains cas :

    List cats = sess.createCriteria(Cat.class)

        .createAlias("kittens", "kt")

        .createAlias("mate", "mt")

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

        .list();

(`createAlias()` ne crée pas de nouvelle instance de `Criteria`.)

Notez que les collections kittens contenues dans les instances de `Cat` retournées par les deux précédentes requêtes ne sont pas pré-filtrées par les critères ! Si vous souhaitez récupérer uniquement les kittens correspondant aux critères, vous devez utiliser `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");

    }

Additionally you may manipulate the result set using a left outer join:

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

This will return all of the `Cat`s with a mate whose name starts with "good" ordered by their mate's age, and all cats who do not have a mate. This is useful when there is a need to order or limit in the database prior to returning complex/large result sets, and removes many instances where multiple queries would have to be performed and the results unioned by java in memory.

Without this feature, first all of the cats without a mate would need to be loaded in one query.

A second query would need to retreive the cats with mates who's name started with "good" sorted by the mates age.

Thirdly, in memory; the lists would need to be joined manually.

## 17.5. Peuplement d'associations de manière dynamique

Vous pouvez spécifier, au moment de l'exécution, le peuplement d'une association en utilisant `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 Section 21.1, « Stratégies de chargement » for more information.

## 17.6. Requêtes par l'exemple

La classe `org.hibernate.criterion.Example` vous permet de construire un critère de requête à partir d'une instance d'objet donnée.

    Cat cat = new Cat();

    cat.setSex('F');

    cat.setColor(Color.BLACK);

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .list();

Les propriétés de type version, identifiant et association sont ignorées. Par défaut, les valeurs null sont exclues.

Vous pouvez ajuster la stratégie d'utilisation de valeurs de l'`Exemple`.

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

Vous pouvez utiliser les "exemples" pour des critères sur des objets associés.

    List results = session.createCriteria(Cat.class)

        .add( Example.create(cat) )

        .createCriteria("mate")

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

        .list();

## 17.7. Projections, agrégation et regroupement

La classe `org.hibernate.criterion.Projections` est une fabrique d'instances de `Projection`. Nous appliquons une projection sur une requête en appelant `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();

Il n'y a pas besoin de "group by" explicite dans une requête par critère. Certains types de projection sont définis pour être des projections de regroupement, qui apparaissent aussi dans la clause SQL `group by`.

Un alias peut optionnellement être assigné à une projection, ainsi la valeur projetée peut être référencée dans des restrictions ou des tris. À cet effet, voici deux procédés différents :

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

Les méthodes `alias()` et `as()` enveloppent simplement une instance de projection dans une autre instance (aliasée) de `Projection`. Pour un raccourci, vous pouvez assigner un alias lorsque vous ajoutez la projection à une liste de projections :

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

Vous pouvez aussi utiliser `Property.forName()` pour formuler des projections :

    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. Requêtes et sous-requêtes détachées

La classe `DetachedCriteria` vous laisse créer une requête en dehors de la portée de la session, et puis l'exécuter plus tard en utilisant une `Session` arbitraire.

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

Les `DetachedCriteria` peuvent aussi être utilisés pour exprimer une sous-requête. Des instances de critère impliquant des sous-requêtes peuvent être obtenues via `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();

Des sous-requêtes corrélées sont également possibles :

    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. Requêtes par identifiant naturel

Pour la plupart des requêtes, incluant les requêtes par critère, le cache de requêtes n'est pas très efficace, parce que l'invalidation du cache de requêtes arrive trop souvent. Cependant, il y existe une requête spéciale où l'on peut optimiser l'algorithme d'invalidation du cache : les recherches par une clef naturelle constante. Dans certaines applications, ce genre de requête se produit fréquemment. L'API des critères fournit une disposition spéciale pour ce cas d'utilisation.

D'abord, vous devrez mapper la clé naturelle de votre entité en utilisant `<natural-id>`, et activer l'utilisation du cache de second niveau.

    <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

    >

Cette fonctionnalité n'est pas prévue pour l'utilisation avec des entités avec des clés naturelles mutables.

Une fois que vous aurez activé le cache de requête d'Hibernate, `Restrictions.naturalId()` vous permettra de rendre l'utilisation de l'algorithme de cache plus efficace.

    session.createCriteria(User.class)

        .add( Restrictions.naturalId()

            .set("name", "gavin")

            .set("org", "hb")

        ).setCacheable(true)

        .uniqueResult();

Copyright © 2004 Red Hat, Inc.

- PrécédentChapitre 16. HQL : langage d'interrogation d'Hibe...
- Niveau supérieur
- Sommaire
- SuivantChapitre 18. SQL natif