You have to override the equals()
and hashCode()
methods if you
intend to put instances of persistent classes in a Set
(the recommended way to represent many-valued associations)
and
intend to use reattachment of detached instances
Hibernate guarantees equivalence of persistent identity (database row) and Java identity
only inside a particular session scope. So as soon as we mix instances retrieved in
different sessions, we must implement equals()
and
hashCode()
if we wish to have meaningful semantics for
Set
s.
The most obvious way is to implement equals()
/hashCode()
by comparing the identifier value of both objects. If the value is the same, both must
be the same database row, they are therefore equal (if both are added to a Set
,
we will only have one element in the Set
). Unfortunately, we can't use that
approach with generated identifiers! Hibernate will only assign identifier values to objects
that are persistent, a newly created instance will not have any identifier value! Furthermore,
if an instance is unsaved and currently in a Set
, saving it will assign
an identifier value to the object. If equals()
and hashCode()
are based on the identifier value, the hash code would change, breaking the contract of the
Set
. See the Hibernate website for a full discussion of this problem. Note
that this is not a Hibernate issue, but normal Java semantics of object identity and equality.
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):
public class Cat { ... public boolean equals(Object other) { if (this == other) return true; if ( !(other instanceof Cat) ) return false; final Cat cat = (Cat) other; if ( !cat.getLitterId().equals( getLitterId() ) ) return false; if ( !cat.getMother().equals( getMother() ) ) return false; return true; } public int hashCode() { int result; result = getMother().hashCode(); result = 29 * result + getLitterId(); return result; } }
Note that a business key does not have to be as solid as a database primary key candidate (see Section 11.1.3, “Considering object identity”). Immutable or unique properties are usually good candidates for a business key.