Tri avec Hibernate

1. Vue d'ensemble

Cet article explique comment trier avec Hibernate , en utilisant à la fois le langage de requête Hibernate (HQL) et l'API Criteria.

2. Tri avec HQL

Le tri avec HQL d'Hibernate est aussi simple que d'ajouter la clause Order By à la chaîne de requête HQL:

String hql = "FROM Foo f ORDER BY f.name"; Query query = sess.createQuery(hql);

Une fois ce code exécuté, Hibernate générera la requête SQL suivante:

Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from FOO foo0_ order by foo0_.NAME

La direction de l'ordre de tri par défaut est ascendante. C'est pourquoi la condition d'ordre, asc , n'est pas incluse dans la requête SQL générée.

2.1. Utilisation d'un ordre de tri explicite

Pour spécifier manuellement l'ordre de tri, vous devrez inclure le sens de l'ordre dans la chaîne de requête HQL :

String hql = "FROM Foo f ORDER BY f.name ASC"; Query query = sess.createQuery(hql);

Dans cet exemple, la définition de la clause asc dans le HQL a été incluse dans la requête SQL générée:

Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from FOO foo0_ order by foo0_.NAME ASC

2.2. Tri par plus d'un attribut

Plusieurs attributs, ainsi qu'un ordre de tri facultatif, peuvent être ajoutés à la clause Order By dans la chaîne de requête HQL:

String hql = "FROM Foo f ORDER BY f.name DESC, f.id ASC"; Query query = sess.createQuery(hql);

La requête SQL générée changera en conséquence:

Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from FOO foo0_ order by foo0_.NAME DESC, foo0_.ID ASC

2.3. Définition de la priorité de tri des valeurs nulles

Par défaut, lorsque l'attribut à trier a des valeurs nulles , c'est au SGBDR de décider de la priorité. Ce traitement par défaut peut être remplacé en plaçant une clause NULLS FIRST ou NULLS LAST dans la chaîne de requête HQL .

Cet exemple simple place les valeurs nulles à la fin de la liste de résultats:

String hql = "FROM Foo f ORDER BY f.name NULLS LAST"; Query query = sess.createQuery(hql);

Voyons la clause is null puis 1 else 0 dans la requête SQL générée :

Hibernate: select foo0_.ID as ID1_1_, foo0_.NAME as NAME2_1_, foo0_.BAR_ID as BAR_ID3_1_, foo0_.idx as idx4_1_ from FOO foo0_ order by case when foo0_.NAME is null then 1 else 0 end, foo0_.NAME

2.4. Tri des relations une à plusieurs

Permet d'analyser un cas de tri complexe: le tri des entités dans une relation un à plusieurs - Barre contenant une collection d' entités Foo .

Nous allons le faire en annotant la collection avec l' annotation Hibernate @OrderBy ; nous préciserons le champ par lequel la commande est effectuée, ainsi que la direction:

@OrderBy(clause = "NAME DESC") Set fooList = new HashSet();

Notez cet argument de clause dans l'annotation. Ceci est unique à @OrderBy d'Hibernate , par rapport à l' annotation JPA @OrderBy similaire . Une autre caractéristique qui différencie cette approche de son équivalent JPA est que l' argument clause indique que le tri est effectué sur la base de la colonne NAME de la table FOO , et non sur l' attribut name de Foo .

Regardons maintenant le tri réel des barres et des foos :

String hql = "FROM Bar b ORDER BY b.id"; Query query = sess.createQuery(hql);

L' instruction SQL résultante montre que les Foo triés sont placés dans une fooList:

Hibernate: select bar0_.ID as ID1_0_, bar0_.NAME as NAME2_0_ from BAR bar0_ order by bar0_.ID Hibernate: select foolist0_.BAR_ID as BAR_ID3_0_0_, foolist0_.ID as ID1_1_0_, foolist0_.ID as ID1_1_1_, foolist0_.NAME as NAME2_1_1_, foolist0_.BAR_ID as BAR_ID3_1_1_, foolist0_.idx as idx4_1_1_ from FOO foolist0_ where foolist0_.BAR_ID=? order by foolist0_.NAME desc

Une chose à garder à l'esprit est qu'il n'est pas possible de trier les listes comme c'était le cas avec JPA. La documentation Hibernate déclare:

«Hibernate ignore actuellement @OrderBy sur @ElementCollection sur par exemple List. L'ordre des éléments est tel que renvoyé par la base de données, non défini. »

En remarque, il serait possible de contourner cette limitation en utilisant la configuration XML héritée pour Hibernate et en remplaçant le élément avec un élément.

3. Tri avec les critères Hibernate

L'API d'objet Criteria fournit la classe Order comme API principale pour gérer le tri.

3.1. Définition de l'ordre de tri

La classe Order a deux méthodes pour définir l'ordre de tri:

  • asc (attribut de chaîne) : trie la requête par attribut dans l'ordre croissant.
  • desc (attribut de chaîne) : trie la requête par attribut dans l'ordre décroissant.

Commençons par un exemple simple - tri par un seul attribut id :

Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.asc("id"));

Notez que l'argument de la méthode asc est sensible à la casse et doit correspondre au nom de l'attribut à trier.

L'API Object de Hibernate Criteria définit explicitement une direction d'ordre de tri et cela se reflète dans l'instruction SQL générée par le code:

Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_ from FOO this_ order by this_.ID sac

3.2. Tri par plus d'un attribut

Le tri par plusieurs attributs ne nécessite que l'ajout d'un objet Order à l' instance Criteria , comme dans l'exemple ci-dessous:

Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.asc("name")); criteria.addOrder(Order.asc("id"));

La requête générée en SQL est:

Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_ from FOO this_ order by this_.NAME asc, this_.ID sac

3.3. Définition de la priorité de tri des valeurs nulles

By default, when the attribute to sort by has null values, it is up to the RDMS to decide the precedence. Hibernate Criteria Object API makes it simple to change that default and place nulls at the end of an ascending ordered list:

Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.asc("name").nulls(NullPrecedence.LAST));

Here is the underlying SQL query – with the is null then 1 else 0 clause:

Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_, this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when this_.NAME is null then 1 else 0 end, this_.NAME asc

Alternatively, we can also place the nulls at the beginning of a descending ordered list:

Criteria criteria = sess.createCriteria(Foo.class, "FOO"); criteria.addOrder(Order.desc("name").nulls(NullPrecedence.FIRST));

The corresponding SQL query follows – with the is null then 0 else 1 clause:

Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_, this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when this_.NAME is null then 0 else 1 end, this_.NAME desc

Note that, if the attribute to sort by is a primitive type like an int, a PresisitenceException will thrown.

Par exemple, si la valeur de f.anIntVariable est nulle, alors l'exécution de la requête:

String jql = "Select f from Foo as f order by f.anIntVariable desc NULLS FIRST"; Query sortQuery = entityManager.createQuery(jql);

lancera:

javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: Null value was assigned to a property of primitive type setter of com.cc.jpa.example.Foo.anIntVariable

4. Conclusion

Cet article explore le tri avec Hibernate - en utilisant les API disponibles pour les entités simples ainsi que pour les entités dans une relation un-à-plusieurs.

L'implémentation de ce didacticiel de tri Hibernate se trouve dans le projet github - il s'agit d'un projet basé sur Eclipse, il devrait donc être facile à importer et à exécuter tel quel.