La clause GROUP BY en Hibernate 4

Comment utiliser la clause SQL GROUP BY avec Hibernate 4 ? C'est ce que nous vous proposons de voir dans cet article.
Florent TRIPIERMis à jour le 27 Nov 2013

Comment utiliser la clause SQL GROUP BY avec Hibernate 4 ?

Rappel théorique

En SQLLangage permettant de communiquer avec une base de données., lorsquune requête possède la clause GROUP BY, les champs mentionnés dans la clause SELECT ne peuvent être que :

  • une colonne sur laquelle porte le GROUP BY ;
  • une fonction dagrégat (qui, elle, peut porter sur nimporte quelle colonne).

En clair, je ne peux pas mentionner dans mon SELECT une colonne sur laquelle je ne groupe pas. La plupart du temps ce genre de requête na aucun sens, mais il arrive que ce soit moins évident. Dans ces cas-là, il faut sen remettre à ses souvenirs de SQL classique.

Pourquoi le préciser ici ? Parce quau même titre que MySQLMoteur de gestion de base de données. est ultra-laxiste sur la syntaxe des requêtes utilisant la clause GROUP BY, Hibernate ne verra pas dinconvénient à générer des requêtes qui nont pas de sens. Mais tous les SGBD ne sont pas comme ça, et sil vous arrivait de passer dun MySQL à Oracle, mieux vaut pour vous que vous nayez pas à réécrire toutes les requêtes de votre application car Oracle, lui, ne plaisante pas avec les GROUP BY.

Le principe

Si vous savez faire une requête simple avec Hibernate 4, lutilisation du GROUP BY va vous paraître un jeu denfant. Il suffit dutiliser la méthode groupBy() de votre CriteriaQuery, à laquelle vous passez en paramètre le champ sur lequel vous voulez grouper.

Exemple : la liste des numéros de commande groupés par eux-même :

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<
String> criteriaQuery = builder.createQuery(String.class);
Root<
Commande> root = criteriaQuery.from(Commande.class);
criteriaQuery.select(root.get(Commande_.numero));
criteriaQuery.groupBy(root.get(Commande_.numero));
TypedQuery<
String> typedQuery = entityManager.createQuery(criteriaQuery);
List<
String> result = typedQuery.getResultList();

Cest seulement pour le principe car cette requête na aucun intérêt. En général on cherche une information qui nest pas spécifiquement dans la colonne du GROUP BY.

En pratique

Un exemple plus pertinent : le nombre de commandes par jour :

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<
Tuple> criteriaQuery = builder.createQuery(Tuple.class);
Root<
Commande> root = criteriaQuery.from(Commande.class);
criteriaQuery.multiselect(root.get(Commande_.date), builder.count(root));
criteriaQuery.groupBy(root.get(Commande_.date));
TypedQuery<
Tuple> typedQuery = entityManager.createQuery(criteriaQuery);
List<
Tuple> result = typedQuery.getResultList();

On a ici un exemple certes un peu complexe mais plus intéressant qui mêle lutilisation du GROUP BY, de la fonction dagrégat COUNT() et du multiselect() (si vous nêtes pas familier avec cette notion, voyez larticle qui lui est dédié).

Remarquez que vous pouvez spécifier plusieurs colonnes de regroupement. Exemple : la liste des commandes groupées par jour et par fournisseur :

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<
Tuple> criteriaQuery = builder.createQuery(Tuple.class);
Root<
Commande> root = criteriaQuery.from(Commande.class);
criteriaQuery.multiselect(root.get(Commande_.date), root.get(Commande_.fournisseur), builder.count(root));
List<
Expression<?>> groupList = new ArrayList();
groupList.add(root.get(Commande_.date));
groupList.add(root.get(Commande_.fournisseur));
criteriaQuery.groupBy(groupList);
TypedQuery<
Tuple> typedQuery = entityManager.createQuery(criteriaQuery);
List<
Tuple> result = typedQuery.getResultList();