Re: [Cantharella-devel] Méthode equals() et hashCode() non redéfinie sur les entités
Quelques questions à la liste pour revenir à la problématique hors projet Cantharella. Étant donné que l'inconvénient de ne pas définir les equals()/hashCode() implique à prendre en compte certains cas particuliers que je préférerais me passer, je me demande quelle serait la stratégie qui comportent le plus d'avantages. Pour mon information, Eric, pourrais-tu me donner plus de détails sur votre façon de générer un id depuis la couche DAO (et même un exemple de classe si possible) ?? La page suivante (https://community.jboss.org/wiki/EqualsAndHashCode) résume bien les différents problèmes qui peuvent survenir suivant les différentes stratégies (cf. "Summary"). La votre n'y est pas présentée, mais elle semble répondre aux quatre problèmes présentés. Le seul inconvénient que j'y vois semble qu'il n'est alors pas possible de garantir au niveau de la base une même logique d'affectation des identifiants (quid des autres moyens de renseigner la BD que via l'application en question ?). J'avais pensé il y a déjà quelques temps à une autre stratégie ou l'on attribue un ID temporaire au niveau de l'application à la création puis que l'ID soit écrasé par la valeur attribué par la BD dès qu'il passe en persistance. Une dernière solution très fréquente est de baser l'equals/hashcode sur les clés métiers. Bien qu'il faille prendre le temps de les définir pour chaque objet, il semblerait que ce soit la solution idéale lorsqu'on est sûr d'avoir une propriété immuable (ou une combinaison) pour le cycle de vie de l'entité. Dans le cas où l'entité n'ait pas de telles propriétés (c'est le cas notamment dans Cantharella), on entend deux sons de cloches : certains semblent dirent qu'il faille trouver une autre statégie, d'autres prétendent que contrairement à une clé primaire, ce n'est pas une obligation que les clés métiers ne changent pas, si cela se produit rarement (cf. extrait de "Java Pertinence with Hibernate" dans le sujet : http://stackoverflow.com/questions/2719877/object-equality-in-context-of-hib...). Si l'on tolère de rares changements, la solution d'ID temporaire, et celle des clés métiers pourraient être toutes deux de bonnes candidates qui s'appliqueraient à tous les cas. Qu'en pensez-vous ? Des avis/préférences sur ces stratégies ? Adrien Le 27/02/2013 17:04, Adrien Cheype a écrit :
Re Eric,
Initialement, le choix avait été fait de ne pas redéfinir les equals() et hashCode() et nous avons mis en place pour Cantharella des façons de contourner les désavantages que cela impliquent. Même si ce n'est pas la solution optimale, je ne pense pas que ce soit une bonne idée de revenir sur ce choix aujourd'hui. Cela impliquerait trop d'effets de bords, par conséquent énormément de tests pour vérifier que l'existant fonctionne toujours correctement et pas mal de modifications de codes.
Pour être plus exact, tous les equals() et hashCode() ne sont pas redéfinis à part pour les @CompositeId (c'est obligatoire) où c'est à chaque fois la même classe paramétré (CompositeId.java) qui est utilisé. Le hashcode et le equals sont alors redéfinis en se basant sur chaque hashcode ou equals de deux entités qui le compose.
Grosso modo, l'inconvénient de ce choix est que deux mêmes objets ne seront pas égaux s'ils proviennent d'une session hibernate différente. Ainsi cela implique que pour comparer deux entités, nous utilisons pas les equals() classiques mais une méthode spécifique qui se basent sur la valeur d'une ou plusieurs propriétés (BeanTools.equals(Object thiz, Object obj, AccessType accessType, String... properties)). De même pour les comparaisons dans une liste, nous n'utilisons pas les contains() et dérivés mais d'autres méthodes statiques de la classe CollectionTools (containsWithValue(...), findWithValue(...), countWithValue(...)).
Dans ton cas particulier de liste déroulante, le problème est que Wicket utilise un contains() pour initialiser la liste avec la bonne valeur. Si la liste dans laquelle il recherche l'entité et cette entité recherchée n'ont pas été chargés dans la même session Hibernate, Wicket ne peut pas faire la correspondance avec le contains(). En générale, nous chargeons les objets dans la même session ce qui évite ce problème.
Nous avons cependant déjà eu plusieurs cas où il nous a fallu contourné le problème. Cela se passe lors d'une saisie multiple lorsqu'on veut précharger certains champs du formulaire avec ceux du précédent formulaire. Tu peux trouver le code suivant dans la classe ManagePurificationPage, les commentaires expliquent ce cas particulier :
// qd saisie multiple avec préremplissage, hack nécessaire afin d'avoir dans le model le même objet que // celui de la liste de choix (sinon comme les objets viennent de sessions hibernate différentes, on n'a pas // l'égalité entre les objets) purificationModel.getObject().setManipulateur( CollectionTools.findWithValue(personnes, "idPersonne", AccessType.GETTER, purificationModel .getObject().getManipulateur().getIdPersonne()));
Tu devrais normalement t'en sortir avec cet astuce... à moins que tu préfères charger ton objet dans la même session Hibernate que ta liste.
Adrien
-- Adrien Cheype Ingénieur en Systèmes d'Information Service « Informatique Scientifique et Appui aux Partenaires du Sud » Direction du Système d'Information (DSI) http://www.ird.fr/dsi/ http://www.ird.fr/informatique-scientifique/
INSTITUT DE RECHERCHE POUR LE DEVELOPPEMENT BP A5 - 98848 Nouméa - Nouvelle Calédonie Tél. +687 260 789
-- Adrien Cheype Ingénieur en Systèmes d'Information Service « Informatique Scientifique et Appui aux Partenaires du Sud » Direction du Système d'Information (DSI) http://www.ird.fr/dsi/ http://www.ird.fr/informatique-scientifique/ INSTITUT DE RECHERCHE POUR LE DEVELOPPEMENT BP A5 - 98848 Nouméa - Nouvelle Calédonie Tél. +687 260 789
Le 27/02/2013 08:17, Adrien Cheype a écrit :
Quelques questions à la liste pour revenir à la problématique hors projet Cantharella.
Étant donné que l'inconvénient de ne pas définir les equals()/hashCode() implique à prendre en compte certains cas particuliers que je préférerais me passer, je me demande quelle serait la stratégie qui comportent le plus d'avantages. Pour mon information, Eric, pourrais-tu me donner plus de détails sur votre façon de générer un id depuis la couche DAO (et même un exemple de classe si possible) ?? Dans notre framework de persistence (ToPIA), les entités existent toujours avec une clé technique, le equals() et hashCode() sont donc basés sur des éléments techniques du framework qui existent dès la création de l'entité: http://maven-site.nuiton.org/topia/topia-persistence/xref/org/nuiton/topia/p... La clé est donc définie par le framework de persistence et non par la base de données.
Et le DAO qui permet de créer l'entité: http://maven-site.nuiton.org/topia/topia-persistence/xref/org/nuiton/topia/p... Dans le cas où c'est un id généré par la base de données, c'est tout de suite plus délicat.
La page suivante (https://community.jboss.org/wiki/EqualsAndHashCode) résume bien les différents problèmes qui peuvent survenir suivant les différentes stratégies (cf. "Summary"). La votre n'y est pas présentée, mais elle semble répondre aux quatre problèmes présentés. Le seul inconvénient que j'y vois semble qu'il n'est alors pas possible de garantir au niveau de la base une même logique d'affectation des identifiants (quid des autres moyens de renseigner la BD que via l'application en question ?).
J'avais pensé il y a déjà quelques temps à une autre stratégie ou l'on attribue un ID temporaire au niveau de l'application à la création puis que l'ID soit écrasé par la valeur attribué par la BD dès qu'il passe en persistance.
Une dernière solution très fréquente est de baser l'equals/hashcode sur les clés métiers. Bien qu'il faille prendre le temps de les définir pour chaque objet, il semblerait que ce soit la solution idéale lorsqu'on est sûr d'avoir une propriété immuable (ou une combinaison) pour le cycle de vie de l'entité. Dans le cas où l'entité n'ait pas de telles propriétés (c'est le cas notamment dans Cantharella), on entend deux sons de cloches : certains semblent dirent qu'il faille trouver une autre statégie, d'autres prétendent que contrairement à une clé primaire, ce n'est pas une obligation que les clés métiers ne changent pas, si cela se produit rarement (cf. extrait de "Java Pertinence with Hibernate" dans le sujet : http://stackoverflow.com/questions/2719877/object-equality-in-context-of-hib...).
Il faudrait être sur de ne pas utiliser l'entité en question dans une collection, le fait que le hashCode change pouvant causer des fuites mémoires.
Qu'en pensez-vous ? Des avis/préférences sur ces stratégies ?
Je ne vois pas d'autres stratégies qui permettent de définir correctement un equals/hashCode simple et propre. -- Éric Chatellier - Code Lutin Tel: 02.40.50.29.28 - http://www.codelutin.com
participants (2)
-
Adrien Cheype -
Eric Chatellier