pühapäev, 8. märts 2015

Hibernate - equals and hashCode pitfails.

Hibernate uses proxies in order to load lazy dependencies.

And one of the common pitfails for me was to be so naive to implement equals()/hashCode() methods in following way:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Person that = (Person) o;

    if (id != null ? !id.equals(that.id) : that.id != null) return false;

    return true;
}

@Override
public int hashCode() {
    return id != null ? id.hashCode() : 0;
}


This code at least has two problems.

First.
It doesn't take in order that lazy loaded classes won't be of Person class. They will be of some Proxy-Generated class that is inherited from Person class.

Second.
Because returned instance is a proxy, not actual class, all of it's values will be null's or default values for primitive types. And the only valid way to interact with proxies is use accessor methods instead of direct field access.


So the proper implementation of equals(Object o)/hashCode() would be:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Person)) return false;

    Person that = (Person) o;

    if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;

    return true;
}

@Override
public int hashCode() {
    return getId() != null ? getId().hashCode() : 0;
}