web-dev-qa-db-fra.com

Dois-je écrire des méthodes equals () et hashCode () dans les entités JPA?

Je souhaite vérifier si l'entité fait partie d'un membre de la collection (@OneToMany ou @ManyToMany) d'une autre entité:

if (entity2.getEntities1().contains(entity1)) { }
55
j2j

Pas nécessairement. Il y a trois options:

  • ne remplacez pas - vous travaillerez donc avec des instances. C'est très bien dans les cas où vous travaillez avec les collections avec uniquement des entités qui sont attachées à la session (et donc garanties d'être la même instance). C'est (pour moi) la voie préférée dans de nombreux cas, car elle nécessite moins de code et moins de considération lors de la substitution

  • remplacer hashCode() et equals() par une clé métier. Il peut s'agir d'un sous-ensemble de propriétés identifiant l'entité. Par exemple, pour un User une bonne clé commerciale peut être le username ou le email. Ceci est considéré comme une bonne pratique.

  • remplacer hashCode() et equals() en utilisant uniquement le champ ID. C'est très bien dans certains cas, surtout si vous avez un identifiant attribué manuellement (comme un UUID). C'est également bien si votre entité n'entrera jamais dans une collection. Mais pour les entités transitoires (sans identifiant) qui entrent dans les collections, cela pose des problèmes, donc soyez prudent avec cette option. Comme l'a noté le seanizer - vous devriez l'éviter. Généralement, toujours, à moins que vous ne sachiez vraiment ce que vous faites (et peut-être que vous le documentiez)

Voir cet article pour plus de détails. Notez également que equals() et hashCode() sont liés et doivent être implémentés tous les deux avec exactement les mêmes champs.

116
Bozho

Oui, vous devriez!

Si vous ne remplacez pas la valeur par défaut Java.lang.Objectequals et hashCode implémentation:

@Entity(name = "Book")
public class Book implements Identifiable<Long> {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    //Getters and setters omitted for brevity
}

l'opération merge renverra une instance d'objet différente et le contrat d'égalité sera rompu comme expliqué dans cet article .

La meilleure façon est d'utiliser une clé métier, comme ceci:

@Entity
public class Book implements Identifiable<Long> {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @NaturalId
    private String isbn;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Book)) return false;
        Book book = (Book) o;
        return Objects.equals(getIsbn(), book.getIsbn());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getIsbn());
    }

    //Getters and setters omitted for brevity
}

Vous pouvez également utiliser l'identifiant pour l'égalité, mais sachez que l'implémentation de hashCode doit toujours renvoyer la même valeur que celle expliquée dans le même article que j'ai déjà mentionné:

@Entity
public class Book implements Identifiable<Long> {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Book)) return false;
        Book book = (Book) o;
        return Objects.equals(getId(), book.getId());
    }

    @Override
    public int hashCode() {
        return 31;
    }

    //Getters and setters omitted for brevity
}
15
Vlad Mihalcea

Oui, vous devez définir les méthodes equals() et hashcode() correspondantes, mais vous ne devez JAMAIS laisser l'id faire partie de l'une ou l'autre. (Voir ma réponse récente dans une question similaire)

13
Sean Patrick Floyd
7
Cid54

Nous avons tendance à laisser IDE générer hashCode() et equals() pour nous. Attention cependant. Lorsque vous générez ces méthodes pour les entités JPA. Certaines versions de equals() vérifie l'identité de la classe

// ... inside equals() - wrong approach for Entities (cause of generate proxies)
if (o == null || this.getClass() != o.getClass()) {
        return false;
}
// ...

Cela casserait vos collections avec certaines bibliothèques JPA car ces bibliothèques créent des proxys pour vos entités (sous-classes), comme par exemple MyGreatEntity_$$_javassist_7 Dans Hibernate.

Dans les entités, autorisez toujours les sous-classes dans equals().

5
zbig