viernes, 1 de febrero de 2008

equals y hashCode en Hibernate

La clase java.lang.Object, de la cual heredan todas las demás clases, define dos métodos muy importantes:

public boolean equals(Object obj)
public int hashCode()

El API de Java describe un conjunto de reglas que deben cumplir ambos métodos en caso de sobreescribirlos, la idea del post no es repetir esos fundamentos teóricos, sino plantear una receta muy sencilla para implementarlos.

Como norma siempre los sobrescribo en todas las entidades usando como clave de comparación una combinación de atributos con significado de negocio (business key) que me asegure la unicidad del objeto.
Si usamos la funcionalidad de id autogenerado (generator class="native"), debemos descartar éste atributo porque es asignado luego de que el objeto es salvado, hasta ese momento el id es null.

Para implementar los métodos me baso en la librería Commons Lang de Apache, es muy probable que ya tengan el jar en el classpath si usan otros frameworks open source, en caso contrario es solo bajarlo del sitio web.

Vamos al grano, por ejemplo si tenemos una entidad Pais (id, codigoISO, nombre), el atributo candidato que nos asegura la unicidad es el código ISO, por lo tanto la implementación sería:


public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(this.codigoISO)
.toHashCode();
}

Instanciamos el HashCodeBuilder con dos números primos diferentes y luego hacemos append de los atributos que definen la business key, en éste caso solamente el codigoISO.

public boolean equals(Object object) {
Pais otroPais = (Pais)object;
return new EqualsBuilder()
.append(this.codigoISO, otroPais.getCodigoISO)
.isEquals();
}

Instanciamos el EqualsBuilder y luego hacemos append de la comparación de los atributos que definen la business key, en éste caso solamente el codigoISO.

Para no tener comportamientos inesperados en la aplicación, es recomendable siempre sobreescribir los métodos equals y hashCode en todas las entidades persistentes, usando ésta librería nos aseguramos que lo hacemos de manera consistente.

Nota: En los ejemplos falta contemplar la posibilidad de NullPointerException o ClassCastException, eso lo dejo como tarea ;-)

Hasta el próximo post!!

No hay comentarios: