Entities equals (), hashCode () et toString (). Comment implémenter correctement?

j'implémente equals() , hashCode() et toString() de mes entités en utilisant tous les champs disponibles dans le haricot.

j'obtiens une Exception INIT paresseuse sur le frontend quand j'essaye de comparer l'égalité ou quand j'imprime l'état obj. C'est parce qu'une liste dans l'entité peut être initialisée paresseusement.

je me demande Quelle est la bonne façon de mettre en œuvre equals() et toString() sur un objet d'entité.

30
demandé sur Bozho 2010-03-15 14:05:48

9 réponses

equals() et "151910920 doit être mis en œuvre à l'aide d'un clé d'entreprise - c'est à dire un ensemble de propriétés qui permet d'identifier l'objet, mais ne sont pas de son auto-ID généré.

dans toString() vous pouvez mettre n'importe quelle information est intéressante - par exemple tous les champs.

utilisez votre IDE (Eclipse, NetBeans, IntelliJ) pour générer tout cela pour vous.

pour éviter LazyInitializationException , Non que ce soit dans equals() ou dans votre vue (jsp), vous pouvez utiliser OpenSessionInView .

16
répondu Bozho 2016-11-10 16:11:34

lorsque vous implémentez les méthodes equals et hashCode pour les objets Hibernate, il est important de

  1. utilisez getters au lieu d'accéder directement aux propriétés de la classe.
  2. ne compare pas directement les classes d'objets, mais utilise instanceof à la place

plus d'information:

Stackoverflow: le remplacement égal-et-hashcode-en-java

documentation D'hibernation: égale et HashCode

Edit: les mêmes règles sur le fait de ne pas accéder aux propriétés de la classe s'appliquent directement à la méthode toString ainsi - seulement en utilisant les getters garantit que l'information réellement contenue dans la classe est retournée.

10
répondu simon 2017-05-23 10:32:33
  1. si deux objets sont égaux, ils doivent avoir le même hashcode.
  2. = () méthode, par défaut, vérifie si deux références se réfèrent à la même instance en mémoire sur le tas de Java

vous pouvez compter sur L'Identificateur de L'entité pour comparer votre entité en utilisant les égaux

public boolean equals(Object o) {
    if(o == null)
        return false;

   Account account = (Account) o;
   if(!(getId().equals(account.getId())))
       return false;

   return true;
}

mais que se passe-t-il lorsque vous avez un Entité non persistante. Ça ne marchera pas parce que son Identifiant n'a pas été affecté.

alors voyons ce Qu'en dit Java Persistence with Hibernate Book

une clé commerciale est une propriété, ou une combinaison de propriétés, qui est unique pour chaque instance avec la même identité de base de données .

Donc

C'est la clé naturelle que vous utiliseriez si vous n'utilisiez pas une clé primaire de substitution à la place.

donc supposons que vous ayez une entité utilisateur et que ses clés naturelles soient prénom et nom de famille (au moins, son prénom et son nom de famille ne changent pas souvent). Il serait donc mis en œuvre en tant que

public boolean equals(Object o) {
    if(o == null)
        return false;

    if(!(o instanceof User))
        return false;

    // Our natural key has not been filled
    // So we must return false;
    if(getFirstName() == null && getLastName() == null)
        return false;

    User user = (User) o;
    if(!(getFirstName().equals(user.getFirstName())))
        return false;

    if(!(getLastName().equals(user.getLastName())))
        return false;

   return true;
}

// default implementation provided by NetBeans
public int hashcode() {
    int hash = 3;

    hash = 47 * hash + ((getFirstName() != null) ? getFirstName().hashcode() : 0)
    hash = 47 * hash + ((getLastName() != null) ? getLastName().hashcode() : 0)

    retrun hash;
}

ça marche très bien! J'utilise même avec des objets Fantaisie comme les dépôts, les services etc

et sur la méthode toString (), comme dit par @Bozho, vous pouvez mettre n'importe quelle information est intéressante. Mais rappelez-vous certains cadres web, comme Wicket et Vaadin, par exemple, utiliser cette méthode pour montrer ses valeurs.

7
répondu Arthur Ronald 2013-08-25 13:40:06

en dehors de ce que les autres ont dit, je pense aussi qu'un objet hiberné doit encore être attaché à la session pour récupérer des informations paresseuses. Sans connexion à une base de données, ces listes ne peuvent pas être chargées:)

0
répondu extraneon 2010-03-15 11:25:13

Ma mise en œuvre de toString() für entités Hibernate est le suivant:

@Override
public String toString() {
    return String.format("%s(id=%d)", this.getClass().getSimpleName(), this.getId());
}

chaque sous-classe de mon AbstractEntity (ci-dessus) l'emporte sur cette méthode si nécessaire:

@Override
public String toString() {
    return String.format("%s(id=%d, name='%s', status=%s)",
            this.getClass().getSimpleName(),
            this.getId(),
            this.getName(),
            this.getStatus());
}

pour hashCode() et equals () gardez à l'esprit que Hibernate utilise souvent des classes de proxy. Donc mes égaux() ressemblent habituellement à ceci:

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;

    Class<AbstractEntity> c1 = Hibernate.getClass(this);
    Class<AbstractEntity> c2 = Hibernate.getClass(obj);
    if (!c1.equals(c2)) return false;

    final AbstractEntity other = (AbstractEntity) obj;
    if (this.getId() == null) {
        if (other.getId() != null) return false;
    }
    else if (!this.getId().equals(other.getId())) return false;

    return true;
}

et comme d'autres l'ont déjà déclaré... soyez prudent avec l'accès paresseux propriétés chargées! Un simple toString () ou même log.debug (entity) peut causer une énorme activité si elle se répercute dans plusieurs objets et propriétés chargés paresseux.

0
répondu Daniel Bleisteiner 2010-03-15 11:55:29

nous implémentons equals() et hashCode() dans notre super classe. Cela fonctionne parfaitement, surtout dans les cartes et les listes, etc. Cela a dû à droite que nous faisons beaucoup de persistance transitive.

égale ():

/**
 * Compare two entity objects, following hibernate semantics for equality. Here we assume that
 * new objects are always different unless they are the same object. If an object is loaded from
 * the database it has a valid id and therefore we can check against object ids.
 *
 * @see com.dolby.persist.bean.EntityObject#equals(java.lang.Object)
 */
@SuppressWarnings("unchecked")
@Override
public final boolean equals(final Object object) {
    if (this == object) return true;
    if (object == null || this.getClass() != object.getClass()) return false;
    final AbstractModelObject<ID> other = (AbstractModelObject<ID>) object;
    if (this.getId() == null || other.getId() == null) return false;
    return this.getId().equals(other.getId());
}

hashCode ():

/**
 * Returns an enttiy objects hashcode.
 * <p>
 * What we are doing here is ensuring that once a hashcode value is used, it never changes for
 * this object. This allows us to use object identity for new objects and not run into the
 * problems.
 * </p>
 * <p>
 * In fact the only case where this is a problem is when we save a new object, keep it around
 * after we close the session, load a new instance of the object in a new session and then
 * compare them.
 * </p>
 * <p>
 * in this case we get A==B but a.hashcode != b.hashcode
 * </p>
 * <p>
 * This will work in all other scenarios and don't lead to broken implementations when the
 * propety of the object are edited. The whole point in generating synthetic primary keys in the
 * first place is to avoid having a primary key which is dependant on an object property and
 * which therefore may change during the life time of the object.
 * </p>
 *
 * @see java.lang.Object#hashCode()
 */
@Override
public final synchronized int hashCode() {
    if (this.hashcodeValue == null) {
        if (this.getId() == null) {
            this.hashcodeValue = new Integer(super.hashCode());
        }
        else {
            final int generateHashCode = this.generateHashCode(this.getId());
            this.hashcodeValue = new Integer(generateHashCode);
        }
    }
    return this.hashcodeValue.intValue();
}
0
répondu Kango_V 2010-05-28 15:32:22

C'est probablement la meilleure et la plus simple façon de le faire:

public String toString() {
    return "userId: " + this.userId + ", firstName: " + this.firstName + ", lastName: " + this.lastName + ", dir: " + this.dir + ", unit: " + this.unit + ", contractExpiryDate: " + this.contractExpiryDate + ", email: " + this.email + ", employeeNumber: " + this.employeeNumber + ", employeeType: " + this.employeeType + ", phone: " + this.phone + ", officeName: " + this.officeName + ", title: " + this.title + ", userType: " + this.userType;
}

public boolean equals(Object obj) {
[...]
return (toString().equals(other.toString()));
}

public int hashCode() {
return toString().hashCode();
}
0
répondu doofus 2015-01-06 17:01:02

S'il vous arrive de surcharger equals () sur des entités Hibernées, assurez-vous de respecter ses contrats: -

  • symétrie
  • réfléchissant
  • TRANSITIVE
  • cohérent
  • NON NULL

et outrepasser hashCode , car son contrat repose sur la mise en œuvre de equals .

Joshua Bloch(le concepteur de la Collection framework) fortement cette règle à suivre

  • article 9: Toujours remplacer hashCode lorsque vous remplacez est égal à

il y a un effet inattendu grave lorsque vous ne respectez pas son contrat. Par exemple List.contains(Object o) pourrait retourner une valeur erronée boolean comme la valeur générale contrat non rempli.

0
répondu Awan Biru 2016-03-02 07:59:44
  1. si vous avez une business key , vous devez utiliser cette option pour equals / hashCode .
  2. si vous n'avez pas de clé d'entreprise, vous ne devriez pas la laisser avec les implémentations par défaut Object égales et hashCode parce que cela ne fonctionne pas après vous merge et entité.
  3. Vous pouvez utiliser l'identifiant d'entité comme il est suggéré dans ce post . Le seul hic, est-ce que vous devez utiliser une implémentation hashCode qui renvoie toujours la même valeur, comme ceci:

    @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 getId() != null && 
               Objects.equals(getId(), book.getId());
        }
    
        @Override
        public int hashCode() {
            return 31;
        }
    
        //Getters and setters omitted for brevity
    }
    
0
répondu Vlad Mihalcea 2018-01-06 10:09:35