Hibernate.org Community Documentation

- Précédent
- Suivant

## Chapitre 16. HQL : langage d'interrogation d'Hibernate

Hibernate fournit un langage d'interrogation extrêmement puissant qui ressemble (et c'est voulu) au SQL. Mais ne soyez pas dupe de la syntaxe ; HQL est totalement orienté objet, cernant des notions comme l'héritage, le polymorphisme et les associations.

## 16.1. Sensibilité à la casse

Les requêtes sont insensibles à la casse, à l'exception des noms de classes Java et des propriétés. Ainsi, `SeLeCT` est identique à `sELEct` et à `SELECT` mais `net.sf.hibernate.eg.FOO` n'est pas identique `net.sf.hibernate.eg.Foo` et `foo.barSet` n'est pas identique à `foo.BARSET`.

Ce guide utilise les mots clés HQL en minuscules. Certains utilisateurs trouvent les requêtes écrites avec les mots clés en majuscules plus lisibles, mais nous trouvons cette convention pénible lorsqu'elle est lue dans du code Java.

## 16.2. La clause from

La requête Hibernate la plus simple est de la forme :

    from eg.Cat

Retourne toutes les instances de la classe `eg.Cat`. Nous n'avons pas besoin de qualifier le nom de la classe, puisque `auto-import` est la valeur par défaut. Donc nous écrivons presque toujours :

    from Cat

Pour pouvoir nous référer à `Cat` dans des autres parties de la requête, vous aurez besoin d'y assigner un alias. Ainsi :

    from Cat as cat

Cette requête assigne l'alias `cat` à l'instance `Cat`, nous pouvons donc utiliser cet alias ailleurs dans la requête. Le mot clé `as` est optionnel. Nous aurions pu écrire :

    from Cat cat

Plusieurs classes peuvent apparaître, ce qui conduira à un produit cartésien (encore appelé jointures croisées).

    from Formula, Parameter

    from Formula as form, Parameter as param

C'est une bonne pratique que de nommer les alias dans les requêtes en utilisant l'initiale en miniscule, ce qui correspond aux standards de nommage Java pour les variables locales (par ex. `domesticCat`).

## 16.3. Associations et jointures

On peut aussi assigner des alias à des entités associées, ou même aux éléments d'une collection de valeurs, en utilisant un `join` (jointure). Par exemple :

    from Cat as cat
        inner join cat.mate as mate
        left outer join cat.kittens as kitten

    from Cat as cat left join cat.mate.kittens as kittens

    from Formula form full join form.parameter param

Les types de jointures supportées sont empruntées de ANSI SQL :

- `inner join`

- `left outer join`

- `right outer join`

- `full join` (jointure ouverte totalement - généralement inutile)

Les constructions des jointures `inner join`, `left outer join` et `right outer join` peuvent être abrégées.

    from Cat as cat
        join cat.mate as mate
        left join cat.kittens as kitten

Nous pouvons soumettre des conditions de jointure supplémentaires en utilisant le mot-clef HQL `with`.

    from Cat as cat
        left join cat.kittens as kitten
            with kitten.bodyWeight
    > 10.0

A "fetch" join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection. It effectively overrides the outer join and lazy declarations of the mapping file for associations and collections. See Section 21.1, « Stratégies de chargement » for more information.

    from Cat as cat
        inner join fetch cat.mate
        left join fetch cat.kittens

Une jointure "fetch" (rapportée) n'a généralement pas besoin de se voir assigner un alias puisque les objets associés ne doivent pas être utilisés dans la clause `where` ou toute autre clause. Notez aussi que les objets associés ne sont pas retournés directement dans le résultat de la requête mais l'on peut y accéder via l'objet parent. La seule raison pour laquelle nous pourrions avoir besoin d'un alias est si nous récupérons récursivement une collection supplémentaire :

    from Cat as cat
        inner join fetch cat.mate
        left join fetch cat.kittens child
        left join fetch child.kittens

Notez que la construction de `fetch` ne peut pas être utilisée dans les requêtes appelées par `scroll()` ou `iterate()`. De même `Fetch` ne devrait pas être utilisé avec `setMaxResults()` ou `setFirstResult()`, ces opérations étant basées sur le nombre de résultats contenant généralement des doublons dès que des collections sont chargées agressivement, par conséquent le nombre de lignes est imprévisible. `Fetch` ne peut pas non plus être utilisé avec une condition `with` ad hoc. Il est possible de créer un produit cartésien par jointure en récupérant plus d'une collection dans une requête, donc faites attention dans ce cas. Récupérer par jointure de multiples collections donne aussi parfois des résultats inattendus pour des mappages de sac, donc soyez prudent lorsque vous formulez vos requêtes dans de tels cas. Finalement, notez que `full join fetch` et `right join fetch` ne sont pas utiles en général.

Si vous utilisez un chargement retardé pour les propriétés (avec une instrumentation par bytecode), il est possible de forcer Hibernate à récupérer les propriétés non encore chargées immédiatement (dans la première requête) en utilisant `fetch all properties`.

    from Document fetch all properties order by name

    from Document doc fetch all properties where lower(doc.name) like '%cats%'

## 16.4. Formes de syntaxes pour les jointures

HQL supporte deux formes pour joindre les associations : `implicit` et `explicit`.

Les requêtes présentes dans la section précédente utilisent la forme `explicit` où le mot clé join est explicitement utilisé dans la clause from. C'est la forme recommandée.

La forme `implicit` n'utilise pas le mot clé join. En revanche, les associations sont "déréférencées" en utilisant la notation. Ces jointures `implicit` peuvent apparaître dans toutes les clauses HQL. Les jointures `implicit` résultent en des jointures internes dans le SQL généré.

    from Cat as cat where cat.mate.name like '%s%'

## 16.5. Faire référence à la propriété identifiant

Il y a en général deux façons de faire référence à une propriété d'identifiant d'une entité :

- La propriété particulière (minuscule) `id` peut être utilisée pour référencer la propriété d'identifiant d'une entité du moment que l'entité ne définisse pas une propriété de non-identifiant appelée id.

- Si l'identité définit une propriété d'identifiant nommée, vous pouvez utiliser ce nom de propriété.

Les références aux propriétés d'identifiant composites suivent les mêmes règles de nommage. Si l'entité a une propriété de non-identifiant appelée id, la propriété d'identifiant composite ne peut être référencée que par son nom défini ; sinon la propriété spéciale `id` peut être utilisée pour référencer la propriété d'identifiant.

## Important

Note : cela a changé de façon significative depuis la version 3.2.2. Dans les versions précédentes, `id` référait toujours à la propriété identifiant quel que soit son nom réel. Une des conséquences de cette décision fut que les propriétés de non-identifiant appelées `id` ne pouvaient jamais être référencées dans les requêtes Hibernate.

## 16.6. La clause select

La clause `select` sélectionne les objets et propriétés qui doivent être retournés dans le résultat de la requête. Soit :

    select mate
    from Cat as cat
        inner join cat.mate as mate

La requête recherchera les `mate` s liés aux `Cat` s. Vous pouvez exprimer cette requête de manière plus compacte :

    select cat.mate from Cat cat

Les requêtes peuvent retourner des propriétés de n'importe quel type de valeur, même celles de type composant :

    select cat.name from DomesticCat cat
    where cat.name like 'fri%'

    select cust.name.firstName from Customer as cust

Les requêtes peuvent retourner de multiples objets et/ou propriétés sous la forme d'un tableau du type `Object[]` :

    select mother, offspr, mate.name
    from DomesticCat as mother
        inner join mother.mate as mate
        left outer join mother.kittens as offspr

Ou sous la forme d'une `List` :

    select new list(mother, offspr, mate.name)
    from DomesticCat as mother
        inner join mother.mate as mate
        left outer join mother.kittens as offspr

Ou bien - à condition que la classe `Family` possède le constructeur approprié - en tant qu'objet typesafe Java :

    select new Family(mother, mate, offspr)
    from DomesticCat as mother
        join mother.mate as mate
        left join mother.kittens as offspr

Vous pouvez assigner des alias aux expressions sélectionnées en utilisant `as` :

    select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
    from Cat cat

C'est surtout utile lorsque c'est utilisé avec `select new map` :

    select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
    from Cat cat

Cette requête retourne une `Map` à partir des alias vers les valeurs sélectionnées.

## 16.7. Fonctions d'agrégation

Les requêtes HQL peuvent aussi retourner les résultats de fonctions d'agrégation sur les propriétés :

    select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
    from Cat cat

Les fonctions d'agrégation supportées sont :

- `avg(...), sum(...), min(...), max(...)`

- `count(*)`

- `count(...), count(distinct ...), count(all...)`

Vous pouvez utiliser des opérateurs arithmétiques, la concaténation, et des fonctions SQL reconnues dans la clause select :

    select cat.weight + sum(kitten.weight)
    from Cat cat
        join cat.kittens kitten
    group by cat.id, cat.weight

    select firstName||' '||initial||' '||upper(lastName) from Person

Les mots clé `distinct` et `all` peuvent être utilisés et ont la même sémantique qu'en SQL.

    select distinct cat.name from Cat cat

    select count(distinct cat.name), count(cat) from Cat cat

## 16.8. Requêtes polymorphiques

Une requête comme :

    from Cat as cat

retourne non seulement les instances de `Cat`, mais aussi celles des sous classes comme `DomesticCat`. Les requêtes Hibernate peuvent nommer n'importe quelle classe ou interface Java dans la clause `from`. La requête retournera les instances de toutes les classes persistantes qui étendent cette classe ou implémente cette interface. La requête suivante retournera tous les objets persistants :

    from java.lang.Object o

L'interface `Named` peut être implémentée par plusieurs classes persistantes :

    from Named n, Named m where n.name = m.name

Notez que ces deux dernières requêtes nécessitent plus d'un SQL `SELECT`. Ce qui signifie que la clause `order by` ne trie pas correctement la totalité des résultats (cela signifie aussi que vous ne pouvez exécuter ces requêtes en appelant `Query.scroll()`).

## 16.9. La clause where

La clause `where` vous permet de réduire la liste des instances retournées. Si aucun alias n'existe, vous pouvez vous référer aux propriétés par leur nom :

    from Cat where name='Fritz'

S'il y a un alias, utilisez un nom de propriété qualifié :

    from Cat as cat where cat.name='Fritz'

Retourne les instances de `Cat` appelé 'Fritz'.

La requête suivante :

    select foo
    from Foo foo, Bar bar
    where foo.startDate = bar.date

retournera les instances de `Foo` pour lesquelles il existe une instance de `bar` avec la propriété `date` égale à la propriété `startDate` de `Foo`. Les expressions de chemin composées rendent la clause `where` extrêmement puissante. Soit :

    from Cat cat where cat.mate.name is not null

Cette requête se traduit en une requête SQL par une jointure interne de table. Si vous souhaitez écrire quelque chose comme :

    from Foo foo
    where foo.bar.baz.customer.address.city is not null

vous finiriez avec une requête qui nécessiterait quatre jointures de table en SQL.

L'opérateur `=` peut être utilisé pour comparer aussi bien des propriétés que des instances :

    from Cat cat, Cat rival where cat.mate = rival.mate

    select cat, mate
    from Cat cat, Cat mate
    where cat.mate = mate

The special property (lowercase) `id` can be used to reference the unique identifier of an object. See Section 16.5, « Faire référence à la propriété identifiant » for more information.

    from Cat as cat where cat.id = 123

    from Cat as cat where cat.mate.id = 69

La seconde requête est particulièrement efficace. Aucune jointure n'est nécessaire !

Les propriétés d'identifiants composites peuvent aussi être utilisées. Supposez que `Person` ait un identifiant composite composé de `country` et `medicareNumber`.

    from bank.Person person
    where person.id.country = 'AU'
        and person.id.medicareNumber = 123456

    from bank.Account account
    where account.owner.id.country = 'AU'
        and account.owner.id.medicareNumber = 123456

Une fois de plus, la seconde requête ne nécessite pas de jointure de table.

See Section 16.5, « Faire référence à la propriété identifiant » for more information regarding referencing identifier properties)

De même, la propriété spéciale `class` accède à la valeur discriminante d'une instance dans le cas d'une persistance polymorphique. Le nom d'une classe Java incorporée dans la clause where sera traduite par sa valeur discriminante.

    from Cat cat where cat.class = DomesticCat

You can also use components or composite user types, or properties of said component types. See Section 16.17, « Composants » for more information.

Un type "any" possède les propriétés particulières `id` et `class`, qui nous permettent d'exprimer une jointure de la manière suivante (là où `AuditLog.item` est une propriété mappée avec `<any>`) :

    from AuditLog log, Payment payment
    where log.item.class = 'Payment' and log.item.id = payment.id

Dans la requête précédente, notez que `log.item.class` et `payment.class` feraient référence à des valeurs de colonnes de la base de données complètement différentes.

## 16.10. Expressions

Les expressions permises dans la clause `where` incluent :

- opérateurs mathématiques : `+, -, *, /`

- opérateurs de comparaison binaire : `=, >=, <=, <>, !=, like`

- opérations logiques : `and, or, not`

- Parenthèses `( )`, indiquant un regroupement

- `in`, `not in`, `between`, `is null`, `is not null`, `is empty`, `is not empty`, `member of` et `not member of`

- Cas simple `case ... when ... then ... else ... end`, et cas "searched", `case when ... then ... else ... end`

- concaténation de chaîne de caractères `...||...` ou `concat(...,...)`

- `current_date()`, `current_time()`, and `current_timestamp()`

- `second(...)`, `minute(...)`, `hour(...)`, `day(...)`, `month(...)`, `year(...)`,

- N'importe quelle fonction ou opérateur défini par EJB-QL 3.0 : `substring(), trim(), lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()`

- `coalesce()` et `nullif()`

- `str()` pour convertir des valeurs numériques ou temporelles vers une chaîne de caractères lisible

- `cast(... as ...)`, où le second argument est le nom d'un type Hibernate, et `extract(... from ...)` si le `cast()` ANSI et `extract()` sont supportés par la base de données sous-jacente

- La fonction HQL `index()`, qui s'applique aux alias d'une collection indexée jointe

- Les fonctions HQL qui prennent des expressions de chemin représentant des collections : `size(), minelement(), maxelement(), minindex(), maxindex()`, ainsi que les fonctions particulières `elements()` et `indices` qui peuvent être quantifiées en utilisant `some, all, exists, any, in`.

- N'importe quelle fonction scalaire SQL supportée par la base de données comme `sign()`, `trunc()`, `rtrim()`, et `sin()`

- Les paramètres de position de JDBC `?`

- paramètres nommés `:name`, `:start_date`, et `:x1`

- SQL textuel `'foo'`, `69`, `6.66E+2`, `'1970-01-01 10:00:01.0'`

- Constantes Java `public static final` `eg.Color.TABBY`

`in` et `between` peuvent être utilisés comme suit :

    from DomesticCat cat where cat.name between 'A' and 'B'

    from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )

Les formes négatives peuvent être écrites ainsi :

    from DomesticCat cat where cat.name not between 'A' and 'B'

    from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )

De même, `is null` et `is not null` peuvent être utilisés pour tester les valeurs nulles.

Les booléens peuvent être facilement utilisés en déclarant les substitutions de requêtes HQL dans la configuration Hibernate :

    <property name="hibernate.query.substitutions"
    >true 1, false 0</property
    >

Ce qui remplacera les mots clés `true` et `false` par `1` et `0` dans la traduction SQL du HQL suivant :

    from Cat cat where cat.alive = true

Vous pouvez tester la taille d'une collection par la propriété particulière `size`, ou la fonction spéciale `size()`.

    from Cat cat where cat.kittens.size
    > 0

    from Cat cat where size(cat.kittens)
    > 0

Pour les collections indexées, vous pouvez faire référence aux indices minimum et maximum en utilisant les fonctions `minindex` et `maxindex`. De manière similaire, vous pouvez faire référence aux éléments minimum et maximum d'une collection de type basique en utilisant les fonctions `minelement` et `maxelement`. Par exemple :

    from Calendar cal where maxelement(cal.holidays)
    > current_date

    from Order order where maxindex(order.items)
    > 100

    from Order order where minelement(order.items)
    > 10000

Les fonctions SQL `any, some, all, exists, in` sont supportées quand l'élément ou l'ensemble des indexes d'une collection (les fonctions `elements` et `indices`) ou le résultat d'une sous requête sont passés (voir ci dessous) :

    select mother from Cat as mother, Cat as kit
    where kit in elements(foo.kittens)

    select p from NameList list, Person p
    where p.name = some elements(list.names)

    from Cat cat where exists elements(cat.kittens)

    from Player p where 3
    > all elements(p.scores)

    from Show show where 'fizard' in indices(show.acts)

Notez que l'écriture de - `size`, `elements`, `indices`, `minindex`, `maxindex`, `minelement`, `maxelement` - peut seulement être utilisée dans la clause where dans Hibernate3.

Les éléments de collections indexées (arrays, lists, maps) peuvent être référencés via index dans une clause where seulement :

    from Order order where order.items[0].id = 1234

    select person from Person person, Calendar calendar
    where calendar.holidays['national day'] = person.birthDay
        and person.nationality.calendar = calendar

    select item from Item item, Order order
    where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11

    select item from Item item, Order order
    where order.items[ maxindex(order.items) ] = item and order.id = 11

L'expression entre `[]` peut même être une expression arithmétique :

    select item from Item item, Order order
    where order.items[ size(order.items) - 1 ] = item

HQL propose aussi une fonction `index()` interne, pour les éléments d'une association un-à-plusieurs ou d'une collection de valeurs.

    select item, index(item) from Order order
        join order.items item
    where index(item) < 5

Les fonctions SQL scalaires supportées par la base de données utilisée peuvent être utilisées :

    from DomesticCat cat where upper(cat.name) like 'FRI%'

Si vous n'êtes pas encore convaincu par tout cela, imaginez la taille et l'illisibilité qui caractériseraient la requête suivante en SQL :

    select cust
    from Product prod,
        Store store
        inner join store.customers cust
    where prod.name = 'widget'
        and store.location.name in ( 'Melbourne', 'Sydney' )
        and prod = all elements(cust.currentOrder.lineItems)

Un indice : cela donne quelque chose comme

    SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
    FROM customers cust,
        stores store,
        locations loc,
        store_customers sc,
        product prod
    WHERE prod.name = 'widget'
        AND store.loc_id = loc.id
        AND loc.name IN ( 'Melbourne', 'Sydney' )
        AND sc.store_id = store.id
        AND sc.cust_id = cust.id
        AND prod.id = ALL(
            SELECT item.prod_id
            FROM line_items item, orders o
            WHERE item.order_id = o.id
                AND cust.current_order = o.id
        )

## 16.11. La clause order by

La liste retournée par la requête peut être triée par n'importe quelle propriété de la classe ou des composants retournés :

    from DomesticCat cat
    order by cat.name asc, cat.weight desc, cat.birthdate

Le mot optionnel `asc` ou `desc` indique respectivement si le tri doit être croissant ou décroissant.

## 16.12. La clause group by

Si la requête retourne des valeurs agrégées, celles-ci peuvent être groupées par propriété d'une classe retournée ou par des composants :

    select cat.color, sum(cat.weight), count(cat)
    from Cat cat
    group by cat.color

    select foo.id, avg(name), max(name)
    from Foo foo join foo.names name
    group by foo.id

Une clause `having` est aussi permise.

    select cat.color, sum(cat.weight), count(cat)
    from Cat cat
    group by cat.color
    having cat.color in (eg.Color.TABBY, eg.Color.BLACK)

Les fonctions SQL et les fonctions d'agrégat sont permises dans les clauses `having` et `order by`, si elles sont prises en charge par la base de données sous-jacente (ce que ne fait pas MySQL par exemple).

    select cat
    from Cat cat
        join cat.kittens kitten
    group by cat.id, cat.name, cat.other, cat.properties
    having avg(kitten.weight)
    > 100
    order by count(kitten) asc, sum(kitten.weight) desc

Notez que ni la clause `group by` ni la clause `order by` ne peuvent contenir d'expressions arithmétiques. Notez aussi qu'Hibernate ne développe pas actuellement une entité faisant partie du regroupement, donc vous ne pouvez pas écrire `group by cat` si toutes les propriétés de `cat` sont non-agrégées. Vous devez lister toutes les propriétés non-agrégées explicitement.

## 16.13. Sous-requêtes

Pour les bases de données supportant les sous-selects, Hibernate supporte les sous requêtes dans les requêtes. Une sous-requête doit être entre parenthèses (souvent pour un appel à une fonction d'agrégation SQL). Même les sous-requêtes corrélées (celles qui font référence à un alias de la requête principale) sont supportées.

    from Cat as fatcat
    where fatcat.weight
    > (
        select avg(cat.weight) from DomesticCat cat
    )

    from DomesticCat as cat
    where cat.name = some (
        select name.nickName from Name as name
    )

    from Cat as cat
    where not exists (
        from Cat as mate where mate.mate = cat
    )

    from DomesticCat as cat
    where cat.name not in (
        select name.nickName from Name as name
    )

    select cat.id, (select max(kit.weight) from cat.kitten kit)
    from Cat as cat

Notez que les sous-requêtes HQL peuvent survenir uniquement dans les clauses select ou where.

Note that subqueries can also utilize `row value constructor` syntax. See Section 16.18, « Syntaxe des constructeurs de valeur de ligne » for more information.

## 16.14. Exemples HQL

Les requêtes Hibernate peuvent être relativement puissantes et complexes. En fait, la puissance du langage d'interrogation est l'un des arguments principaux de vente de Hibernate. Voici quelques exemples très similaires aux requêtes que nous avons utilisées lors d'un récent projet. Notez que la plupart des requêtes que vous écrirez seront plus simples que les exemples qui suivent.

La requête suivante retourne l'id de commande, le nombre d'articles et la valeur totale de la commande pour toutes les commandes non payées d'un client particulier pour une valeur totale minimum donnée, ces résultats étant triés par la valeur totale. La requête SQL générée sur les tables `ORDER`, `ORDER_LINE`, `PRODUCT`, `CATALOG` et `PRICE` est composée de quatre jointures internes ainsi que d'un sous-select (non corrélé).

    select order.id, sum(price.amount), count(item)
    from Order as order
        join order.lineItems as item
        join item.product as product,
        Catalog as catalog
        join catalog.prices as price
    where order.paid = false
        and order.customer = :customer
        and price.product = product
        and catalog.effectiveDate < sysdate
        and catalog.effectiveDate
    >= all (
            select cat.effectiveDate
            from Catalog as cat
            where cat.effectiveDate < sysdate
        )
    group by order
    having sum(price.amount)
    > :minAmount
    order by sum(price.amount) desc

Quel monstre ! En principe, dans des situations réelles, nous n'approuvons pas les sous-requêtes, notre requête ressemblait donc plutôt à ce qui suit :

    select order.id, sum(price.amount), count(item)
    from Order as order
        join order.lineItems as item
        join item.product as product,
        Catalog as catalog
        join catalog.prices as price
    where order.paid = false
        and order.customer = :customer
        and price.product = product
        and catalog = :currentCatalog
    group by order
    having sum(price.amount)
    > :minAmount
    order by sum(price.amount) desc

La requête suivante compte le nombre de paiements pour chaque statut, en excluant tout paiement dans le statut `AWAITING_APPROVAL` où le changement de statut le plus récent à été fait par l'utilisateur courant. En SQL, cette requête effectue deux jointures internes et un sous-select corrélé sur les tables `PAYMENT`, `PAYMENT_STATUS` et `PAYMENT_STATUS_CHANGE`.

    select count(payment), status.name
    from Payment as payment
        join payment.currentStatus as status
        join payment.statusChanges as statusChange
    where payment.status.name <
    > PaymentStatus.AWAITING_APPROVAL
        or (
            statusChange.timeStamp = (
                select max(change.timeStamp)
                from PaymentStatusChange change
                where change.payment = payment
            )
            and statusChange.user <
    > :currentUser
        )
    group by status.name, status.sortOrder
    order by status.sortOrder

Si nous avions mappé la collection `statusChanges` comme une liste, au lieu d'un ensemble, la requête aurait été plus facile à écrire.

    select count(payment), status.name
    from Payment as payment
        join payment.currentStatus as status
    where payment.status.name <
    > PaymentStatus.AWAITING_APPROVAL
        or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <
    > :currentUser
    group by status.name, status.sortOrder
    order by status.sortOrder

La prochaine requête utilise la fonction de serveur MS SQL `isNull()` pour retourner tous les comptes et paiements impayés pour l'organisation à laquelle l'utilisateur courant appartient. Elle est traduite en SQL par trois jointures internes, une jointure externe ainsi qu'un sous-select sur les tables `ACCOUNT`, `PAYMENT`, `PAYMENT_STATUS`, `ACCOUNT_TYPE`, `ORGANIZATION` et `ORG_USER`.

    select account, payment
    from Account as account
        left outer join account.payments as payment
    where :currentUser in elements(account.holder.users)
        and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
    order by account.type.sortOrder, account.accountNumber, payment.dueDate

Pour certaines bases de données, nous devons éliminer le sous-select (corrélé).

    select account, payment
    from Account as account
        join account.holder.users as user
        left outer join account.payments as payment
    where :currentUser = user
        and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
    order by account.type.sortOrder, account.accountNumber, payment.dueDate

## 16.15. Nombreuses mises à jour et suppressions

HQL now supports `update`, `delete` and `insert ... select ...` statements. See Section 15.4, « Opérations de style DML » for more information.

## 16.16. Trucs & Astuces

Vous pouvez compter le nombre de résultats d'une requête sans les retourner :

    ( (Integer) session.createQuery("select count(*) from ....").iterate().next() ).intValue()

Pour trier les résultats par la taille d'une collection, utilisez la requête suivante :

    select usr.id, usr.name
    from User as usr
        left join usr.messages as msg
    group by usr.id, usr.name
    order by count(msg)

Si votre base de données supporte les sous-selects, vous pouvez placer des conditions sur la taille de la sélection dans la clause where de votre requête :

    from User usr where size(usr.messages)
    >= 1

Si votre base de données ne supporte pas les sous-selects, utilisez la requête suivante :

    select usr.id, usr.name
    from User usr
        join usr.messages msg
    group by usr.id, usr.name
    having count(msg)
    >= 1

Cette solution ne peut pas retourner un `User` avec zéro message à cause de la jointure interne, la forme suivante peut donc être utile :

    select usr.id, usr.name
    from User as usr
        left join usr.messages as msg
    group by usr.id, usr.name
    having count(msg) = 0

Les propriétés d'un JavaBean peuvent être injectées dans les paramètres nommés d'une requête :

    Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");

    q.setProperties(fooBean); // fooBean has getName() and getSize()

    List foos = q.list();

Les collections sont paginables via l'utilisation de l'interface `Query` avec un filtre :

    Query q = s.createFilter( collection, "" ); // the trivial filter

    q.setMaxResults(PAGE_SIZE);

    q.setFirstResult(PAGE_SIZE * pageNumber);

    List page = q.list();

Les éléments d'une collection peuvent être triés ou groupés en utilisant un filtre de requête :

    Collection orderedCollection = s.filter( collection, "order by this.amount" );

    Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );

Vous pouvez récupérer la taille d'une collection sans l'initialiser :

    ( (Integer) session.createQuery("select count(*) from ....").iterate().next() ).intValue();

## 16.17. Composants

Les composants peuvent être utilisés dans presque tous les cas comme les types de valeur dans les requêtes HQL. Ils peuvent apparaître dans la clause `select` comme ce qui suit :

    select p.name from Person p

    select p.name.first from Person p

où la propriété de nom de Person est un composant. Des composants peuvent aussi être utilisés dans la clause `where` :

    from Person p where p.name = :name

    from Person p where p.name.first = :firstName

Des composants peuvent être utilisés dans la clause `order by` :

    from Person p order by p.name

    from Person p order by p.name.first

Another common use of components is in row value constructors.

## 16.18. Syntaxe des constructeurs de valeur de ligne

HQL supporte l'utilisation de la syntaxe `row value constructor` SQL ANSI (aussi appelée syntaxe `tuple`), bien que la base de données sous-jacente ne supporte pas nécessairement cette notion. Là, nous faisons généralement référence à des comparaisons multi-valuées, typiquement associées à des composants. Considérez une entité Person qui définit un composant de nom :

    from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt'

Voici une syntaxe valide, bien que quelque peu fastidieuse. Pour la rendre plus concise, utilisez la syntaxe `row value constructor` :

    from Person p where p.name=('John', 'Jingleheimer-Schmidt')

Il est utile de spécifier cela dans la clause `select` :

    select p.name from Person p

Alternativement, utiliser la syntaxe `row value constructor` peut être avantageux quand vous utilisez des sous-requêtes nécessitant une comparaison avec des valeurs multiples :

    from Cat as cat
    where not ( cat.name, cat.color ) in (
        select cat.name, cat.color from DomesticCat cat
    )

Si vous décidez d'utiliser cette syntaxe, il vous faudra prendre en considération le fait que la requête sera dépendante de la commande des sous-propriétés du composant dans les métadonnées.

Copyright © 2004 Red Hat, Inc.

- PrécédentChapitre 15. Traitement par lot
- Niveau supérieur
- Sommaire
- SuivantChapitre 17. Requêtes par critères