Offrez-vous les deux auteurs du blog, Greg et Mimie ==> Ils créent votre site et le référencent pour vous !

Hibernate : Pagination, Tri et Unicité avec Criteria

de Mimie le 9 janvier 2011

Rubrique : Programmation

Créer une requête Hibernate avec l’API Criteria devant retourner un ensemble d’éléments uniques en tenant compte d’une pagination et des tris, n’est pas si aisé. D’ailleurs la FAQ d’Hibernate nous le confirme lorsqu’on lit ceci :

  1. Hibernate does not return distinct results using for a query with outer join fetching enabled for a collection
  2. One day Hibernate might be smart enough to know that if you call setFirstResult() or setMaxResults() it should not use a join, but a second SQL SELECT. Try it, your version of Hibernate might already be smart enough. If not, write two queries, one for limiting stuff, the other for eager fetching
© http://www.packtpub.com

© http://www.packtpub.com

Je vais vous exposer la solution que j’utilise depuis peu qui permet de n’effectuer que deux requêtes en base.

Problème à résoudre

Imaginons la requête suivante qui doit récupérer les 25 decks uniques à partir du 50ème dont la saison est le numéro 502, le tout trié par nom de deck. Instinctivement voici la requête Criteria qui nous vient à l’esprit :

Criteria criteria = getSession().createCriteria(Deck.class);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.createAlias("season", "dbSeason");
criteria.add(Restrictions.in("dbSeason.pkSeason", new Integer[] { 502 }));
criteria.setFirstResult(50);
criteria.setMaxResults(25);
criteria.addOrder(Order.asc("name"));
return criteria.list();

Malheureusement ceci ne va retourner les decks attendus, les méthodes setFirstResult et setMaxResults ne sont pas compatibles avec la méthode setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).

Voici donc un moyen de contourner ceci.

Solution

La solution consiste à appliquer à la lettre la FAQ d’Hibernate citée plus haut, construire une requête en deux temps :

  1. la requête principale va se contenter de récupérer uniquement les identifiants de decks correspondant aux critères en tenant compte de la pagination et des tris,
  2. la sous-requête va récupérer l’intégralité des decks en utilisant la restriction in. Ne pas oublier d’appliquer à nouveau les tris car sinon l’ordre des decks récupérés n’est pas conservé.
Criteria searchCriteria = getSession().createCriteria(Deck.class);
searchCriteria.setProjection(Projections.distinct(Projections.id()));
searchCriteria.createAlias("season", "dbSeason");
searchCriteria.add(Restrictions.in("dbSeason.pkSeason", new Integer[] { 502 }));
searchCriteria.setFirstResult(50);
searchCriteria.setMaxResults(25);
searchCriteria.addOrder(Order.asc("name"));
List<Integer> decksIdList = searchCriteria.list();
List<Deck> decksList = new ArrayList<Deck>();
if (!decksIdList.isEmpty()) {
     Criteria selectCriteria = getSession().createCriteria(Deck.class);
     selectCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
     selectCriteria.add(Restrictions.in("pkDeck", decksIdList));
     selectCriteria.addOrder(Order.asc("name"));
     decksList = selectCriteria.list();
}
return decksList;

Peut faire mieux ?

Je n’ai pas réussi à tout faire en une seule requête en utilisant SubCriteria et la restriction SubQueries.propertyIn car les méthodes setMaxResults et setFirstResult ne sont pas applicable à celle-ci.
Si quelqu’une a réussi ou à une meilleure solution, ça m’intéresse :)

Cet article a été écrit par :

– qui a déjà rédigé 130 posts sur Des Geeks et des lettres.

Passionné d'informatique et développeur JavaEE de métier, je me consacre principalement à écrire des billets sur les sujets du Web et de la programmation Web. Ce blog est un espace qui me permet de partager mes découvertes avec vous et me sert accessoirement de pense bête !

Contacter l'auteur

{ 2 commentaires… à vous de vous exprimer ! }

1 Greg janvier 9, 2011 à 23 h 27 min

Espérons que les gens pourront te venir en aide :)

Répondre

2 Mimie avril 19, 2011 à 11 h 50 min

Je suis bien déçu de ne pas avoir eu un autre point de vue sur ce sujet …

Répondre

Laissez un Commentaire

Article précédent:

Article suivant: