¿Cómo se deben implementar los valores iguales y el código hash de la clase modelo en Hibernate? ¿Cuáles son los errores comunes? ¿Es la implementación predeterminada lo suficientemente buena para la mayoría de los casos? ¿Tiene sentido utilizar claves comerciales?
Me parece que es bastante difícil hacerlo bien para que funcione en todas las situaciones, cuando se tienen en cuenta la búsqueda diferida, la generación de identificaciones, el proxy, etc.
Respuestas:
Hibernate tiene una descripción larga y agradable de cuándo / cómo anular
equals()
/hashCode()
en la documentaciónLa esencia de esto es que solo debe preocuparse por si su entidad será parte de una
Set
o si va a separar / adjuntar sus instancias. Este último no es tan común. El primero generalmente se maneja mejor a través de:equals()
/hashCode()
en una clave de negocio - por ejemplo, una combinación única de atributos que no va a cambiar durante la vida útil del objeto (o, al menos, la sesión).equals()
/hashCode()
en la clave principal SI está configurada y la identidad del objeto / de loSystem.identityHashCode()
contrario. La parte importante aquí es que debe volver a cargar 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 (que en última instancia resultará en errores y / o corrupción de datos) porque su entidad puede estar asignada a un depósito que no coincide con su actualhashCode()
.fuente
refresh()
? ¿Cómo termina su entidad, que obedece elSet
contrato, en el depósito equivocado (suponiendo que tenga una implementación de código hash suficientemente buena)?Set.contains(entity)
y volveráfalse
. Lo mismo ocurre con get () / put () / etc ...No creo que la respuesta aceptada sea precisa.
Para responder a la pregunta original:
La respuesta es sí, en la mayoría de los casos lo es.
Solo necesita anular
equals()
yhashcode()
si la entidad se usará en unSet
(que es muy común) Y la entidad se separará y, posteriormente, se volverá a adjuntar a 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 alguno condiciones es verdadera.
fuente
La mejor
equals
/hashCode
aplicación es cuando se utiliza una clave de negocio único .La clave comercial debe ser coherente en todas las transiciones de estado de la entidad (transitoria, adjunta, separada, eliminada), por eso 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 forma, puede utilizar el UUID para
equals
/hashCode
porque la identificación se asigna antes de que la entidad se vacíe.Incluso puede usar el identificador de entidad para
equals
yhashCode
, pero eso requiere que siempre devuelva el mismohashCode
valor para asegurarse de que el valor de la entidad hashCode sea coherente en todas las transiciones de estado de entidad. Consulte esta publicación para obtener más información sobre este tema .fuente
BaseEntity
y no vuelvas a pensar en ese problema. Se necesita un poco de espacio en el lado de la base de datos, pero es mejor que pague ese precio por la comodidad :)Cuando una entidad se carga a través de la carga diferida, no es una instancia del tipo base, sino un subtipo generado dinámicamente generado por javassist, por lo tanto, una verificación en el mismo tipo de clase fallará, así que no use:
en su lugar use:
que también es una buena práctica, como se explica en Implementación de iguales en Prácticas Java .
por la misma razón, al acceder directamente a los campos, puede que no funcione y devuelva un valor nulo, en lugar del valor subyacente, por lo que no use la comparación en las propiedades, sino use los captadores, ya que pueden desencadenarse para cargar los valores subyacentes.
fuente
Sí, es difícil. En mi proyecto, equals y hashCode se basan en la identificación del objeto. El problema de esta solución es que ninguno de ellos funciona si el objeto aún no se ha persistido, ya que la identificación la genera la base de datos. En mi caso, eso es tolerable ya que en casi todos los casos los objetos persisten de inmediato. Aparte de eso, funciona muy bien y es fácil de implementar.
fuente
En la documentación de Hibernate 5.2, dice que es posible que no desee implementar hashCode y es igual en absoluto, dependiendo de su situación.
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode
Generalmente, dos objetos cargados desde la misma sesión serán iguales si son iguales en la base de datos (sin implementar hashCode y equals).
Se complica si usa dos o más sesiones. En este caso, la igualdad de dos objetos depende de la implementación del método igual.
Además, se meterá en problemas si su método equals está comparando ID que solo se generan mientras persiste un objeto por primera vez. Es posible que aún no estén allí cuando se llame a iguales.
fuente
Hay un artículo muy bueno aquí: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html
Citando una línea importante del artículo:
En lenguaje sencillo
fuente
Si anuló
equals
, asegúrese de cumplir con sus contratos: -Y anular
hashCode
, ya que su contrato depende de laequals
implementación.Joshua Bloch (diseñador del marco de la colección) instó encarecidamente a seguir estas reglas.
Hay efectos no deseados graves cuando no sigue estos contratos. Por ejemplo,
List#contains(Object o)
podría devolver unboolean
valor incorrecto ya que el contrato general no se cumplió.fuente