La JPA hashCode() / equals() dilemme

Il y a eu certains les discussions ici sur des entités JPA et qui hashCode()/equals() mise en œuvre devrait être utilisé pour les classes d'entité JPA. La plupart (si pas tous) d'entre eux dépendent de mise en veille prolongée, mais j'aimerais en discuter JPA-mise en œuvre-neutre (je suis en utilisant EclipseLink, par la voie).

Toutes les implémentations possibles ont leur propre avantages et inconvénients sujet:

  • hashCode()/equals() contrat conformité (immuabilité) pour List/Set opérations
  • Si identiques objets (par exemple, à partir de différentes sessions, des proxies dynamiques de paresseusement-chargé de structures de données) peuvent être détectés
  • Si les entités se comportent correctement dans détaché (ou non persistantes) état

Aussi loin que je peux voir, il y a trois options:

  1. Ne pas les remplacer; compter sur Object.equals() et Object.hashCode()
    • hashCode()/equals() travail
    • ne peut pas identifier les objets identiques, les problèmes avec des proxies dynamiques
    • pas de problèmes avec du recul des entités
  2. Remplacer, à partir de la clé primaire
    • hashCode()/equals() sont cassés
    • identité correcte (pour toutes les entités gérées)
    • problèmes avec du recul des entités
  3. Remplacer, à partir de la d'Affaires-Id (non champs de clé primaire; ce sur les clés étrangères?)
    • hashCode()/equals() sont cassés
    • identité correcte (pour toutes les entités gérées)
    • pas de problèmes avec du recul des entités

Mes questions sont:

  1. Ai-je raté une option et/ou pro/con point?
  2. Quelle option avez-vous choisi et pourquoi?

Mise à JOUR 1:

Par "hashCode()/equals() sont cassé", je veux dire que les hashCode() invocations peuvent renvoyer des valeurs différentes, ce qui est (lorsqu'il est correctement mis en œuvre) ne se décompose pas dans le sens de la Object documentation de l'API, mais qui pose des problèmes lorsque vous essayez de récupérer un changement de l'entité à partir d'un Map, Set ou d'autres à base de hachage Collection. Par conséquent, les implémentations JPA (au moins EclipseLink) ne fonctionnera pas correctement dans certains cas.

Mise à JOUR 2:

Merci pour vos réponses -- la plupart d'entre eux ont une qualité remarquable.

Malheureusement, je suis toujours pas sûr de l'approche qui sera le meilleur pour une vraie vie de l'application, ou la façon de déterminer la meilleure approche pour mon application. Donc, je vais garder ouverte la question et d'espoir pour certains plus de discussions et/ou des opinions.

  • Je ne comprends pas ce que tu veux dire par "hashCode()/equals() cassé"
  • de l'Objet.hashCode() javadoc: "Chaque fois qu'il est invoqué sur le même objet plus d'une fois lors d'une exécution d'une application Java, la méthode hashCode doit constamment revenir par le même entier" et "Si deux objets sont égaux selon la equals(Object) méthode, puis en appelant la méthode hashCode sur chacun des deux objets doit produire le même résultat sous forme d'entier." qui est violé dans l'option 2 et 3.
  • Qu'ils ne serait pas "cassé" en ce sens, alors, comme dans l'option 2 et 3, vous serait mise en œuvre à la fois equals() et hashCode() en utilisant la même stratégie.
  • b option 2 du hashCode() va changer après persist(), l'option 3 du hashCode() va changer si certaines modifications de champ que je qualifierais de "cassé" dans le sens de la méthode equals() javadoc commentaire et Joshua Blochs livre "Effective Java".
  • Ce n'est pas vrai de l'option 3. hashCode() et equals() doit être en utilisant les mêmes critères, donc si l'un de vos champs de changement, oui le hashcode() la méthode renverra une valeur différente pour la même instance que ce précédent a fait, mais il en sera de equals(). Vous ai quitté la seconde partie de la phrase à partir de la hashcode() javadoc: à Chaque fois qu'elle est invoquée sur le même objet plus d'une fois lors d'une exécution d'une application Java, la méthode hashCode doit constamment revenir par le même entier, pas fourni d'informations utilisés dans les égaux des comparaisons sur l'objet est modifié.
  • b Peut-être que je suis missunderstanding le contrat (je ne suis pas un locuteur natif anglais) mais je suis interprète "pas fourni d'informations utilisés dans les égaux des comparaisons sur l'objet est modifié" "il y a une exception à la règle si les champs ont été modifiés sur lequel la méthode equals ne repose pas sur les" qui n'est pas vrai dans l'option 3, parce que les deux reposent sur le même champs (qui tout peut changer).
  • En fait la partie de la phrase signifie le contraire - l'appel de hashcode() sur la même instance d'objet doit retourner la même valeur, à moins que tous les champs utilisés dans la equals() la mise en œuvre du changement. En d'autres termes, si vous avez trois champs dans votre classe et votre equals() méthode utilise seulement deux d'entre eux pour déterminer l'égalité des cas, alors vous pouvez vous attendre le hashcode() valeur de retour à changer si vous modifiez l'une de ces valeurs du champ - qui a du sens lorsque l'on considère que cette instance de l'objet n'est plus "égale" à la valeur que l'ancienne instance représentée.
  • Merci, matt-b - j'ai mis à jour ma question pour refléter votre correctes.
  • "des problèmes lorsque vous essayez de récupérer un changement de l'entité à partir d'une Carte, ou autre base de hachage Collections"... ce doit être "des problèmes lorsque vous essayez de récupérer un changement de l'entité à partir d'une table de hachage, HashSet ou d'autres à base de hachage Collections"
  • Quelles sont vos pensées sur y compris votre verrouillage optimiste bien, si vous l'utilisez dans votre hashCode()? Peut-être cela mérite sa propre question.
  • Qu'entendez-vous des problèmes avec du recul des entités?
  • Si vous ajoutez un pas-encore-ont persisté entité dans une base de hachage collection et si l'entité hashCode s'appuie sur le PK, son hashcode va changer après conservation (parce que sa PK sera probablement set), d'où la violation de la base de hachage collection du contrat, ce qui conduit à des résultats imprévisibles
  • n'est pas encore persisté n'est pas le même que détaché. Selon l'APC, spec, "une entité détachée de l'instance est une instance avec un persistantes identité qui n'est pas (ou plus) associée à un contexte de persistance". Merci de corriger la question. Aussi, quand vous dites des problèmes avec mais-pas-a persisté entités, tu veux dire en fait la cause de la hashCode/equals() broken problème. Ainsi, vous pourriez envisager de reformuler les trois points.
  • Qu'entendez-vous par problems with dynamic proxies?
  • Si j'ai bien compris les réponses correctement alors il y a une 4ème non cotées de l'option qui a les avantages de tous les 3 options cotées. C'est lorsque vous créez une entité (l'appel de nouveau) vous pouvez également affecter un grand nombre aléatoire à son JPA ID (avec la valeur par défaut GenerationType attribué). Maintenant vous pouvez utiliser une égale/hashcode basée sur ce nombre aléatoire. La probabilité que deux de ces nombres aléatoires entrent en collision est faible.
  • Ce n'est pas correct. Vous faites probablement référence à la génération d'UUID - cela devrait vous donner une image plus claire. onjava.com/pub/a/onjava/2006/09/13/...
  • Belle question. Est-ce encore s'applique sur JPA en 2019?

InformationsquelleAutor MRalwasser | 2011-02-17