Chapitre 14. HQL: Langage de requêtage d'Hibernate

Hibernate fourni un langage d'interrogation extrêmement puissant qui ressemble (et c'est voulu) au SQL. Mais ne soyez pas distraits par la syntaxe ; HQL est totalement orienté objet, comprenant des notions d'héritage, de polymorphisme et d'association.

14.1. Sensibilité à la casse

Les requêtes sont insensibles à la casse, à l'exception des noms des 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 minuscule. Certains utilisateurs trouvent les requêtes écrites avec les mots clés en majuscule plus lisibles, mais nous trouvons cette convention pénible lorsqu'elle est lue dans du code Java.

14.2. La clause from

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

from eg.Cat

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

from Cat

La plupart du temps, vous devrez assigner un alias puisque vous voudrez faire référence à Cat dans d'autres parties de la requête.

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 a le mérite d'être en phase avec les standards de nommage Java pour les variables locales (domesticCat).

14.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).

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 celles de ANSI SQL

  • inner join (jointure fermée)

  • left outer join (jointure ouverte par la gauche)

  • right outer join (jointure ouverte par la droite)

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

Les constructions des jointures inner join, left outer join et right outer join peuvent être abbré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

Par ailleurs, une jointure "fetchée" (rapportée) permet d'initialiser les associations ou collections de valeurs en même temps que leur objet parent, le tout n'utilisant qu'un seul Select. Ceci est particulièrement utile dans le cas des collections. Ce système permet de surcharger les déclarations "lazy" et "outer-join" des fichiers de mapping pour les associations et collections. Voir Section 19.1, « Stratégies de chargement » pour plus d'informations.

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

Une jointure "fetchée" (rapportée) n'a généralement pas besoin de se voir assigner un alias puisque les objets associés n'ont pas à être utilisés dans les autres clauses. 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érions 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(). fetch ne devrait pas non plus être utilisé avec setMaxResults() ou setFirstResult(), ces opérations étant basées sur le nombre de résultats qui contient généralement des doublons dès que des collections sont chargées. 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 mappings de bag, 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%'

14.4. Formes de syntaxes pour les jointures

HQL supporte deux formes pour joindre les associations: implicite et explicite.

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

La forme implicite n'utilise pas le mot clé join. A la place, les associations sont "déréférencées" en utilisant le notation '.'. Ces jointures peuvent apparaitre dans toutes les clauses. Les jointures implicites résultent en des inner join dans le SQL généré.

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

14.5. 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 mates liés aux Cats. Vous pouvez explimer la requête d'une manière plus compacte :

select cat.mate from Cat cat

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

select cat.name from DomesticCat cat
where cat.name like 'fri%'
select cust.name.firstName from Customer as cust

Les requêtes peuvent retourner plusieurs 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 sous la forme d'un objet Java typé,

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

à condition que la classe Family possède le constructeur approprié.

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.

14.6. Fonctions d'aggrégation

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

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

Les fonctions 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 signification qu'en SQL.

select distinct cat.name from Cat cat

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

14.7. Requêtes polymorphiques

Une requête comme:

from Cat as cat

retourne non seuleument 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 SELECT SQL. 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()).

14.8. 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 dont name est égale à 'Fritz'.

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 est égale à la propriété startDate de Foo. Les expressions utilisant la navigation rendent la clause where extrêmement puissante. Soit :

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

Cette requête se traduit en SQL par une jointure interne à une 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 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

La propriété spéciale (en minuscule) id peut être utilisée pour faire référence à l'identifiant d'un objet (vous pouvez aussi utiliser le nom de cette propriété).

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'un identifiant composé peuvent aussi être utilisées. Supposez que Person ait un identifiant 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 même, la propriété spéciale class interroge 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

Vous pouvez aussi spécifier les propriétés des composants ou types utilisateurs composés (components, composite user types etc). N'essayez jamais d'utiliser un expression de navigation qui se terminerait par une propriété de type composant (qui est différent d'une propriété d'un composant). Par exemple, si store.owner est une entité avec un composant address

store.owner.address.city    // okay
store.owner.address         // error!

Un type "any" possède les propriétés spéciales id et class, qui nous permettent d'exprimer une jointure de la manière suivante (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.

14.9. Expressions

Les expressions permises dans la clause where incluent la plupart des choses que vous pouvez utiliser en SQL :

  • opérateurs mathématiques +, -, *, /

  • opérateur de comparaison binaire =, >=, <=, <>, !=, like

  • opérateurs 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 and not member of

  • "Simple" case, case ... when ... then ... else ... end, and "searched" case, case when ... then ... else ... end

  • concatenation de chaîne de caractères ...||... ou concat(...,...)

  • current_date(), current_time(), current_timestamp()

  • second(...), minute(...), hour(...), day(...), month(...), year(...),

  • N'importe quel 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 s'appliquent expressions représentant des collections : size(), minelement(), maxelement(), minindex(), maxindex(), ainsi que les fonctions spéciales elements() et indices qui peuvent être quantifiées en utilisant some, all, exists, any, in.

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

  • Les paramètres positionnels de JDBC ?

  • paramètres nommés :name, :start_date, :x1

  • littéral SQL 'foo', 69, '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' )

et la forme négative peut être écrite

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

Les booléens peuvent être facilement utilisés en déclarant les substitutions de requêtes 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é spéciale 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 and maxindex. De manière similaire, vous pouvez faire référence aux éléments minimum et maximum d'une collection de type basiques en utilisant les fonctions minelement et maxelement.

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 supportent que leur soient passées l'élément, l'index d'une collection (fonctions elements et indices) ou le résultat d'une sous requête (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 - peuvent 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 one-to-many ou d'une collections 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 transformation SQL de la requête HQL suivante :

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 donnerait 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
    )

14.10. La clause order by

La liste retounée par la requête peut être triée par n'importe quelle propriété de la classe ou du composant retourné :

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.

14.11. La clause group by

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

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

14.12. Sous-requêtes

Pour les bases de données le supportant, 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 arriver seulememnt dans les clauses select ou where.

Pour des sous-requêtes avec plus d'une expression dans le select, vous pouvez utiliser un constructeur de tuples :

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

Notez que sur certaines bases de données (mais par Oracle ou HSQL), vous pouvez utiliser des constructeurs de tuples dans d'autres contextes, par exemple lors du requêtage de composants ou de types utilisateur composites :

from Person where name = ('Gavin', 'A', 'King')

Ce qui est équivalent à la forme plus verbeuse suivante :

from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')

Il y a deux bonnes raisons que vous ne puissiez ne pas vouloir faire cette sorte de choses : d'abord, ce n'est pas complètement portable entre les plateformes de base de données ; deuxièmement, la requête est maintenant dépendante de l'ordre des propriétés dans le document de mapping.

14.13. Exemples HQL

Les requêtes Hibernate peuvent être relativement puissantes et complexes. En fait, la puissance du langage de requêtage est l'un des avantages principaux d'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 suivantes !

La requête suivante retourne l'id de commande (order), le nombre d'articles (items) et la valeur totale de la commande (order) pour toutes les commandes non payées d'un client (customer) particulier pour un total minimum donné, le tout trié 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 interne ainsi que d'une sous-requête (non corrélée).

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, nous ne sommes pas très fan des sous-requêtes, la requête ressemblait donc plutôt à cela :

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 (payments) pour chaque status, en excluant les paiements dans le status AWAITING_APPROVAL où le changement de status le plus récent à été fait par l'utilisateur courant. En SQL, cette requête effectue deux jointures internes et des sous requêtes corrélées 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 requête qui suit utilise la fonction de MS SQL isNull() pour retourner tous les comptes (accounts) et paiements (payments) impayés pour l'organisation à laquelle l'uilisateur (user) courant appartient. Elle est traduite en SQL par trois jointures internes, une jointure externe ainsi qu'une sous requête 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 d'autres base de données, nous aurions dû faire sans la sous-requête (corrélée).

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

14.14. Mise à jour et suppression

HQL supporte maintenant les expressions update, delete et insert ... select .... Voir Section 13.4, « Opérations de style DML » pour les détails.

14.15. Trucs & Astuces

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

( (Integer) session.iterate("select count(*) from ....").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-requêtes, 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-requêtes, utilisez la requête suivante :

select usr.id, usr.name
from User usr.name
    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'un 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.iterate("select count(*) from ....").next() ).intValue();