Friday, August 20, 2010

Equals implementation for Lazy loaded objects in Hibernate

Most of you already know about the standard way to implement equals and hashCode for a class. As an example lets consider a User class


public class User {
private Long id;
private String name;
private String address;
}




If we consider Id as unique identifier for logical equality of users then the equals and hashCode method can be written over "id", as given below


@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}


@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}



Now lets assume User is an entity thats managed by hibernate and is lazily loaded. So actually you will be interacting with the proxy(HibernateProxy) of User and not the actual User class.

Now in this the equals implementation of User class will fail. If we check for equality of normal instance of User class with the proxy instance of User the equals method will return false. The reason for failure is the class check i.e


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


Since the class of Proxy user object is not User but a HibernateProxy class.


To overcome this problem hibernate comes with a utility class HibernateProxyHelper which is used to get the actual class if the object passed to it is of type HibernateProzy. The above piece of code can be replaced with

if (  HibernateProxyHelper.getClassWithoutInitializingProxy(obj) != getClass() ) {
return false;
}


Another problem with the implementation of equals in case of proxy objects is we cannot directly access the fields i.e

if (!id.equals(other.id))
return false;


as proxy objects can access only the methods of the underlying object so instead of directly accessing the field we have to use the getter method


if (!id.equals(other.getId()))
return false;


So the updated equals implemention that should work perfectly fine in case of proxy objects also will be as given below


public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (  HibernateProxyHelper.getClassWithoutInitializingProxy(obj) != getClass() ) {
return false;
}
User other = (User) obj;
if (id == null) {
if (other.getId() != null)
return false;
} else if (!id.equals(other.getId()))
return false;
return true;
}


EmotionsEmoticon