Don't Let Hibernate Steal Your Identity
Subject:   Getters must be used internally
Date:   2009-02-26 08:13:04
From:   fivetenwill
This is a fantastic solution and has removed a lot of developers' worries in coming up with proper equals and hashCode methods. One thing that I discovered is that if you access id directly, instead of using getId() in the equals and hashCode methods, the real id (the one persisted in the DB) is not loaded, and you get the random one assigned when the proxied instance was created. This might a result of using javassist (which is default in the current version of Hibernate) instead of CGLib, since I just discovered this issue and we've just upgraded our Hibernate libraries. Maybe it could be an issue in CGLib'ed Hibernate as well? I know there have been differences in behaviour with non-id members being accessed directly (and the member would contain null) vs. using the getter (which the proxy would intercept, set the member to the correct value, and retun the correct value) in CGLib proxies.

Here's the modified implementations which use getId() instead of direct access.

public boolean equals(Object o) {
if (this == o) return true;
if (o == null ||
!(o instanceof PersistentObject)) {

return false;

PersistentObject other
= (PersistentObject)o;

// if the id is missing, return false
if (getId() == null) return false;

// equivalence by id
return getId().equals(other.getId());

public int hashCode() {
if (getId() != null) {
return getId().hashCode();
} else {
return super.hashCode();

public String toString() {
return this.getClass().getName()
+ "[id=" + getId() + "]";