|By Michael Muller||
|April 26, 2013 10:00 AM EDT||
Hibernate is one of the most used ORM Java frameworks out there. It is really simple to use, just add few annotations and you're ready to go. However, it is also really easy to experience strange behaviors and bugs if you don't respect Hibernate's best practices. That's why at Tocea we developed rules to detect coding mistakes and to make sure that your experience with Hibernate will be painless.
Our Hibernate repository currently contains 30 rules. Some of them are related to the detection of possible bugs, other treat of maintainability or performance. In this article, we're going to present four rules that deal with a famous Hibernate's usage problem : the implementation of equals() and hashCode() methods.
What's the problem?
Equals and hashCode are used to compare objects. The default implementation of equals compares the object's address in memory. This is good as long as your objects are in memory, but Hibernate's goal is precisely to move them out of memory. Within a Hibernate session, Hibernate takes care of managing objects equality between a persistent identity and a Java identity.
However, when dealing with objects that come from different sessions, Hibernate can't do it. That's why it is necessary to override equals and hashCode.
This is our rule n°1 : HbEntitiesMustImplementEqualsHashCode detects any Hibernate entity that does not implement both methods. We also have HbEmbeddableEntitiesMustImplementEqualsHashCodethat does the same for embeddable entities.
First step to solve it : implement equals()
So, I have a User and I want to implement the equals() method. I can just use its Id...
Ooops, or can't I? Indeed, if your Id is generated (as it is likely to be), it will be assigned once the object is persisted. Before that, all non-persisted entities have the same null id. So you could experience strange equality results... The good solution is to rely on the objects business fields to compare them, like the first name and the last name of my user.
This is our rule n°3 : HbEntitiesEqualsMustAvoidIDField detects usage of id field in equals().
Second step to solve it : implement hashCode()
I'm now ready to implement hashCode(). The Java language requires that if a.equals(b), then a.hashCode() is the same as b.hashCode(). As a consequence, I must use the same fields in hashCode() as I used in equals(). This may seem trivial for simple entities, but through evolution of an entity, this can be easy to forget to add a new field to one of the method.
This is our rule n°4 : HbEntityEqualsAndHashCodeUseDifferentFields detects equals and hashCode methods that use different fields.
To sum up, with this four rules you should be able to prevent a good deal of bugs related to incorrect usage of equals and hashCode in Hibernate entities. Have you already encountered such problems? Do you think there should be more rules regarding the implementation of these two methods? Please let us know in the comments!
Oh, and we have presented four of our thirty Hibernate rules, so that leaves plenty of interesting stuff to talk about : stay tuned!