Comment les equals et hashcode de la classe de modèle doivent-ils être implémentés dans Hibernate? Quels sont les écueils courants? L'implémentation par défaut est-elle suffisante pour la plupart des cas? Y a-t-il un sens à utiliser des clés professionnelles?
Il me semble qu'il est assez difficile de faire fonctionner correctement dans toutes les situations, lorsque la récupération paresseuse, la génération d'identifiants, le proxy, etc. sont pris en compte.
Réponses:
Hibernate a une belle et longue description de quand / comment remplacer
equals()
/hashCode()
dans la documentationL'essentiel est que vous ne devez vous en soucier que si votre entité fera partie d'un
Set
ou si vous allez détacher / attacher ses instances. Ce dernier n'est pas si courant. Le premier est généralement mieux géré via:equals()
/hashCode()
sur une clé métier - par exemple une combinaison unique d'attributs qui ne changera pas pendant la durée de vie de l'objet (ou, au moins, de la session).equals()
/hashCode()
sur la clé primaire SI elle est définie et l'identité de l'objet /System.identityHashCode()
sinon. La partie importante ici est que vous devez recharger votre ensemble une fois qu'une nouvelle entité a été ajoutée et persistante; Sinon, vous risquez de vous retrouver avec un comportement étrange (entraînant finalement des erreurs et / ou une corruption de données) car votre entité peut être allouée à un compartiment ne correspondant pas à son couranthashCode()
.la source
refresh()
? Comment votre entité, qui obéit auSet
contrat, se retrouve-t-elle dans le mauvais compartiment (en supposant que vous ayez une implémentation de hashcode suffisamment bonne).Set.contains(entity)
et vous reviendrezfalse
. Il en va de même pour get () / put () / etc ...Je ne pense pas que la réponse acceptée soit exacte.
Pour répondre à la question initiale:
La réponse est oui, dans la plupart des cas.
Vous avez seulement besoin de remplacer
equals()
ethashcode()
si l'entité sera utilisée dans unSet
(ce qui est très courant) ET l'entité sera détachée des sessions de mise en veille prolongée, puis rattachée à celles-ci (ce qui est une utilisation inhabituelle de la mise en veille prolongée).La réponse acceptée indique que les méthodes doivent être remplacées si l' une des conditions est vraie.
la source
La meilleure
equals
/hashCode
implémentation est lorsque vous utilisez une clé métier unique .La clé métier doit être cohérente dans toutes les transitions d'état d'entité (transitoire, attachée, détachée, supprimée), c'est pourquoi vous ne pouvez pas compter sur l'id pour l'égalité.
Une autre option consiste à passer à l'utilisation des identificateurs UUID , attribués par la logique d'application. De cette façon, vous pouvez utiliser l'UUID pour le
equals
/hashCode
car l'id est attribué avant que l'entité ne soit vidée.Vous pouvez même utiliser l'identificateur d'entité pour
equals
ethashCode
, mais cela vous oblige à toujours renvoyer la mêmehashCode
valeur afin de vous assurer que la valeur de hachage d'entité est cohérente dans toutes les transitions d'état d'entité. Consultez cet article pour en savoir plus sur ce sujet .la source
BaseEntity
et ne pensez plus jamais à ce problème. Cela prend un peu de place côté db mais ce prix-là, vous feriez mieux de payer pour le confort :)Lorsqu'une entité est chargée via le chargement paresseux, ce n'est pas une instance du type de base, mais un sous-type généré dynamiquement généré par javassist, donc une vérification sur le même type de classe échouera, donc n'utilisez pas:
utilisez plutôt:
ce qui est également une bonne pratique, comme expliqué dans Implémentation d'égaux dans les pratiques Java .
pour la même raison, accéder directement aux champs peut ne pas fonctionner et retourner null, au lieu de la valeur sous-jacente, donc n'utilisez pas de comparaison sur les propriétés, mais utilisez les getters, car ils pourraient déclencher le chargement des valeurs sous-jacentes.
la source
Ouais, c'est dur. Dans mon projet, equals et hashCode reposent tous deux sur l'id de l'objet. Le problème de cette solution est qu'aucune d'elles ne fonctionne si l'objet n'a pas encore été persistant, car l'identifiant est généré par la base de données. Dans mon cas, c'est tolérable car dans presque tous les cas, les objets sont conservés immédiatement. En dehors de cela, cela fonctionne très bien et est facile à mettre en œuvre.
la source
Dans la documentation d'Hibernate 5.2, il est dit que vous ne voudrez peut-être pas du tout implémenter hashCode et equals - en fonction de votre situation.
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode
Généralement, deux objets chargés à partir de la même session seront égaux s'ils sont égaux dans la base de données (sans implémenter hashCode et equals).
Cela se complique si vous utilisez deux sessions ou plus. Dans ce cas, l'égalité de deux objets dépend de votre implémentation de méthode equals.
De plus, vous aurez des problèmes si votre méthode equals compare des ID qui ne sont générés que lors de la persistance d'un objet pour la première fois. Ils ne sont peut-être pas encore là quand égaux est appelé.
la source
Il y a un très bel article ici: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
Citant une ligne importante de l'article:
En termes simples
la source
S'il vous arrive de passer outre
equals
, assurez-vous de respecter ses contrats: -Et passer outre
hashCode
, car son contrat repose surequals
mise œuvre.Joshua Bloch (concepteur du cadre de collection) a vivement recommandé que ces règles soient respectées.
Il y a un effet involontaire grave lorsque vous ne suivez pas ces contrats. Par exemple, il
List#contains(Object o)
peut renvoyer uneboolean
valeur erronée car le contrat général n'est pas respecté.la source