¿Cómo se deben implementar equals y hashcode cuando se usa JPA e Hibernate?

8 minutos de lectura

¿Como se deben implementar equals y hashcode cuando se usa
egaga

¿Cómo se deben implementar los valores iguales y el código hash de la clase modelo en Hibernate? ¿Cuáles son las trampas comunes? ¿La implementación predeterminada es lo suficientemente buena para la mayoría de los casos? ¿Tiene algún sentido utilizar claves comerciales?

Me parece que es bastante difícil hacer que funcione correctamente en cada situación, cuando se tienen en cuenta la búsqueda perezosa, la generación de id, el proxy, etc.

  • Consulte también stackoverflow.com/a/39827962/548473 (implementación spring-data-jpa)

    – Grigori Kislin

    03 oct.

1643706127 197 ¿Como se deben implementar equals y hashcode cuando se usa
ChssPly76

Hibernate tiene una buena y larga descripción de cuándo / cómo anular equals() / hashCode() en documentación

La esencia de esto es que solo necesita preocuparse si su entidad será parte de un Set o si va a separar/adjuntar sus instancias. Esto último no es tan común. El primero generalmente se maneja mejor a través de:

  1. Fundamento equals() / hashCode() en una clave comercial, por ejemplo, una combinación única de atributos que no va a cambiar durante la vida útil del objeto (o, al menos, de la sesión).
  2. Si lo anterior es imposible, base equals() / hashCode() en la clave principal SI está configurada y la identidad del objeto / System.identityHashCode() de lo contrario. El importante parte aquí es que usted necesita recargar su conjunto después de que se le haya agregado y persistido una nueva entidad; de lo contrario, puede terminar con un comportamiento extraño (lo que en última instancia resulta en errores y/o corrupción de datos) porque su entidad puede estar asignada a un depósito que no coincide con su actual hashCode().

  • Cuando dices “recargar” @ChssPly76 te refieres a hacer un refresh()? ¿Cómo su entidad, que obedece a la Set el contrato termina en el cubo equivocado (suponiendo que tenga una implementación de código hash lo suficientemente buena).

    – no seguidor

    28 oct. 09 a las 19:21

  • Actualizar la colección o recargar toda la entidad (propietaria), sí. En lo que respecta al depósito incorrecto: a) agrega una nueva entidad para configurar, su identificación aún no está configurada, por lo que está utilizando el código de identidad que coloca su entidad en el depósito n. ° 1. b) su entidad (dentro del conjunto) persiste, ahora tiene una identificación y, por lo tanto, está usando hashCode () en función de esa identificación. Es diferente de arriba y tendría colocó su entidad en el cubo #2. Ahora, suponiendo que tiene una referencia a esta entidad en otro lugar, intente llamar Set.contains(entity) y volverás false. Lo mismo ocurre con get() / put() / etc…

    – ChssPly76

    28 oct. 09 a las 19:33

  • Tiene sentido, pero nunca usé IdentityHashCode, aunque lo veo usado en la fuente de Hibernate como en sus ResultTransformers.

    – no seguidor

    29 oct.

  • Al usar Hibernate, también podría encontrarse con este problema, a la que todavía no he encontrado una solución.

    – Giovanni Botta

    18 jul.

  • @ ChssPly76 Debido a las reglas comerciales que determinan si dos objetos son iguales, necesitaré basar mis métodos equals/hashcode en propiedades que pueden cambiar durante la vida útil de un objeto. ¿Es eso realmente un gran problema? Si es así, ¿cómo puedo evitarlo?

    – ubiquibacón

    20 nov.

No creo que la respuesta aceptada sea precisa.

Para responder a la pregunta original:

¿La implementación predeterminada es lo suficientemente buena para la mayoría de los casos?

La respuesta es sí, en la mayoría de los casos lo es.

Solo necesita anular equals() y hashcode() si la entidad se utilizará en un Set (que es muy común) Y la entidad se desconectará y, posteriormente, se volverá a conectar a las sesiones de hibernación (que es un uso poco común de hibernación).

La respuesta aceptada indica que los métodos deben anularse si cualquiera condición es verdadera.

  • Esto se alinea con mi observación, es hora de averiguarlo. por qué.

    – Vlastimil Ovčáčík

    11 feb. 16 a las 16:31


  • “Solo necesita anular equals() y hashcode() si la entidad se utilizará en un conjunto” es suficiente si algunos campos identifican un objeto, por lo que no desea confiar en Object.equals() para identificar objetos.

    – davidxxx

    11 nov.

¿Como se deben implementar equals y hashcode cuando se usa
Vlad Mihalcea

El mejor equals y hashCode la implementación es cuando usa una clave comercial única o un identificador natural, como este:

@Entity
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    @Column(unique = true, updatable = false)
    private String name;
 
    @Override
    public int hashCode() {
        HashCodeBuilder hcb = new HashCodeBuilder();
        hcb.append(name);
        return hcb.toHashCode();
    }
 
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Company)) {
            return false;
        }
        Company that = (Company) obj;
        EqualsBuilder eb = new EqualsBuilder();
        eb.append(name, that.name);
        return eb.isEquals();
    }
}

La clave comercial debe ser consistente en todas las transiciones de estado de la entidad (transitoria, adjunta, separada, eliminada), es por eso que no puede confiar en la identificación para la igualdad.

Otra opción es cambiar al uso de identificadores UUID, asignados por la lógica de la aplicación. De esta manera, puede usar el UUID para el equals/hashCode porque la identificación se asigna antes de que la entidad se vacíe.

Incluso puede utilizar el identificador de entidad para equals y hashCode, pero eso requiere que siempre devuelvas lo mismo [hashCode value so that you make sure that the entity hashCode value is consistent across all entity state transitions, like this:

@Entity(name = "Post")
@Table(name = "post")
public class Post implements Identifiable<Long> {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String title;
 
    public Post() {}
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
 
        if (!(o instanceof Post))
            return false;
 
        Post other = (Post) o;
 
        return id != null &&
               id.equals(other.getId());
    }
 
    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
  
    //Getters and setters omitted for brevity
}

  • +1 for the uuid approach. Put that into a BaseEntity and never think again about that problem. It takes a bit of space on the db side but that price you better pay for the comfort 🙂

    – Martin Frey

    Aug 25 ’16 at 13:49

¿Como se deben implementar equals y hashcode cuando se usa
stivlo

When an entity is loaded through lazy loading, it’s not an instance of the base type, but is a dynamically generated subtype generated by javassist, thus a check on the same class type will fail, so don’t use:

if (getClass() != that.getClass()) return false;

instead use:

if (!(otherObject instanceof Unit)) return false;

which is also a good practice, as explained on Implementing equals in Java Practices.

for the same reason, accessing directly fields, may not work and return null, instead of the underlying value, so don’t use comparison on the properties, but use the getters, since they might trigger to load the underlying values.

Yeah, it’s hard. In my project equals and hashCode both rely on the id of the object. The problem of this solution is that neither of them works if the object has not been persisted yet, as the id is generated by database. In my case that’s tolerable since in almost all cases objects are persisted right away. Other than that, it works great and is easy to implement.

  • What I think we did is to use object identity in the case where the id has not been generated

    – Kathy Van Stone

    Oct 28 ’09 at 17:22

  • the problem here is that if you persist the object, your hashcode changes. That can have big detrimental results if the object is already part of a hash based data structure. So, if you do wind up using object identity, you’d better continue using obj id until the object is completely freed (or remove the object from any hash based structures, persist, then add it back in). Personally, I think it would be best to not use id, and base the hash on immutable properties of the object.

    – Kevin Day

    Oct 29 ’09 at 3:55

1643706128 268 ¿Como se deben implementar equals y hashcode cuando se usa
Nina

In the documentation of Hibernate 5.2 it says you might not want to implement hashCode and equals at all – depending on your situation.

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

Generally, two objects loaded from the same session will be equal if they are equal in the database (without implementing hashCode and equals).

It gets complicated if you’re using two or more sessions. In this case, the equality of two objects depends on your equals-method implementation.

Further, you’ll get into trouble if your equals-method is comparing IDs that are only generated while persisting an object for the first time. They might not be there yet when equals is called.

  • What I think we did is to use object identity in the case where the id has not been generated

    – Kathy Van Stone

    Oct 28 ’09 at 17:22

  • the problem here is that if you persist the object, your hashcode changes. That can have big detrimental results if the object is already part of a hash based data structure. So, if you do wind up using object identity, you’d better continue using obj id until the object is completely freed (or remove the object from any hash based structures, persist, then add it back in). Personally, I think it would be best to not use id, and base the hash on immutable properties of the object.

    – Kevin Day

    Oct 29 ’09 at 3:55

1643706128 804 ¿Como se deben implementar equals y hashcode cuando se usa
Ravi Shekhar

There is very nice article here: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

Quoting an important line from the article:

We recommend implementing equals() and hashCode() using Business key
equality. Business key equality means that the equals() method
compares only the properties that form the business key, a key that
would identify our instance in the real world (a natural candidate
key):

In simple terms

public class Cat {

...
public boolean equals(Object other) {
    //Basic test / class cast
    return this.catId==other.catId;
}

public int hashCode() {
    int result;

    return 3*this.catId; //any primenumber 
}

}

.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad