Les collections sont un vieux serpent de mer avec Hibernate et doivent être traitées très spécifiquement, contrairement aux autres entités. En fait leur mauvaise compréhension est une source majeure de bugs aussi nous creusons le sujet ici.
Débloque les + belles offres tech en 10 mins
Hibernate implémente un mécanisme de dirty checking qui permet de savoir lorsqu’un commit de transaction a lieu quelles sont les entités à mettre à jour en base. Ce mécanisme repose sur le traditionnel equals()
de Java, et si le equals()
ne correspond pas à ce qui était dans le cache d’Hibernate les entités managées par Hibernate (autrement dit en état persistant pour les puristes) sont mises à jour en base. Ceci explique notamment qu’il n’y a absolument pas besoin de lancer un saveOrUpdate
sur une entité déjà managée par Hibernate lorsque vous la modifiez et que vous êtes dans un contexte transactionnel.
Malheureusement pour nous, il existe un vilain petit canard dans tout ce beau monde, à savoir les collections, où justement tout est géré par référence.
Les mappings les plus courants de collections passent par un @OneToMany
du côté du propriétaire de la liaison et un @ManyToOne
de l’autre côté, ce dernier définissant la colonne de table faisant la liaison. Sauf que bien souvent il n’y a aucune raison de rendre la liaison navigable dans les deux sens, et généralement c’est le sens ManyToOne
qui a plus de raisons de voir l’autre côté de la liaison que l’inverse. Donc dans ce cas le fait d’implémenter l’autre sens risque surtout d’amener des soucis comme le fameux problème N + 1 d’Hibernate, qui se manifeste quand on voit des centaines de selects identiques apparaître en base.
Autrement dit avant d’implémenter un @OneToMany
réfléchissez bien au sens dans lequel votre liaison doit être navigable (et que ce n’est pas pratique d’implémenter un findByXXX sur le DAO de l’autre côté de la liaison), vous éviterez bien des soucis.
Alors vous me direz qu’il serait possible de remplacer directement la collection en elle-même, par exemple en passant par un setter. Sauf qu’en fait ça ne marche pas non plus, car Hibernate considèrera alors que toutes les entités stockées dans la collection sont… nouvelles ! Et là à vous les joies des doublons et autres. Pour vous en convaincre mettez un point d’arrêt dans votre code au moment de sauvegarder un objet contenant une collection @OneToMany
stockée dans une instance de java.util.Set
, et vous constaterez que votre joli HashSet
est en fait un org.hibernate.collections.PersistentSet
!!!
Bon alors comme vous insistez vraiment, je vais vous fournir l’algorithme général de merge de collections en Hibernate, à savoir :
session.delete()
.Alors oui c’est horriblement complexe, mais comme je suis sympa j’ai créé un petit projet sur GitHub qui contient le nécessaire pour faire ça sans douleur. Regardez la classe GojulHibernateCollectionsMergeTool
(et oui le préfixe Gojul sert avant tout à éviter les collisions de nommage car je suis convaincu d’être le seul imbécile à avoir eu l’idée de nommer ses classes de la sorte… :D). Pour l’instant la classe ne fonctionne que sur des instances de java.util.Set
mais si à l’avenir le besoin apparaît elle pourrait fonctionner aussi sur des instances de java.util.List
.
Débloque les + belles offres tech en 10 mins
Cet article vous a plu ? Vous aimerez sûrement aussi :
Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.
Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.
Changer d’entreprise, c’est excitant. Nouveau challenge, nouveaux collègues, nouveau café. Mais, bien souvent, on oublie…
Ça n’étonnera personne si nous affirmons que le monde du développement logiciel est en constante…
En Allemagne, le travail en tandem à temps partiel, aussi appelé « jobsharing » est…
On se retrouve comme d'habitude pour le début du classement qcm saison automne ! Mais…
La saison printemps des tests techniques WeLoveDevs s'est terminée le 31 mai, et c'est Axel…