Please notice that this guide does not intend to cover Hibernate/JPA at all, but only to provide some best practices. Personally I learned Hibernate using Hibernate in Action.
I really appreciate any comments saying what do you think about this practice, why it's wrong, and what additional practices should be added.
So let's start:
Override hashCode() and equals()The Hibernate reference states that "It is recommended that you implement equals() and hashCode() to compare the natural key properties of the entity."
The reason is simple: different instances of the class may represent the same record in the database. Therefore, when comparing these two instances, you'd like to get equal result, while default equals implementation will return not-equal since it's not the same instance.
This becomes really important, when working with collections, especially with Sets. You don't want the same object to appear twice in set, right?
Overriding the hashCode() and equals() is not a very complex goal, but you must be very careful:
1. Remember that two equal objects must return the same hash code. Therefore, you cannot use auto-generated Hibernate id in hashCode() - this value is not assigned for the newly created objects. After persistence occurs, the value is assigned, so the hashCode will change, when the object actually wasn't changed!
2. Changing the fields that participate in hashCode() will change the hash code value. So if your object is stored in a Set (or it's key of a Map), you won't be able to retrieve it from the set anymore - one hash was used for insert and another one was used for retrieve.
So basically you'll need to remember not to change objects that are stored in sets! And this is really important!
So you may ask: how will I know who stored my object in a set?
My answer is simple: you cannot know this, unless you don't give your objects out. So you are the only person who is using these objects, so you know how they are kept, right?
Storing the objects without giving them out is not so weird idea: keep the persistence layer away from the business logic and return a copy of object when required.
Another option: return immutable objects to the business tier. So the business tier won't be able to change them. When the change is required - provide a special API. Thus the objects won't change accidentally.
Try to Make All Object ImmutableThis may sound weird, how exactly the persistent objects can be immutable. But in the previous part I described why it's important. And actually it can be quite easily achieved:
1. Make all setters private. Thus it will be impossible to call them without using reflection. (Hibernate will use reflection and populate the properties during the object retrieval)
2. When returning collections, wrap them with Collections.unmodifiable. So the user won't be able to modify your collections.
3. Allow changes only via special methods.
Return Copy of Persistent Objects to the Business TierSo no accidental change in hash code may occur.
Additionally when filling the business objects, a lot of potential problems may be resolved. Consider that the persistent object contains a lazy collection. If the object is returned as is to the business tier, the lazy elements in the collections can be accessed after the transaction was closed, therefore the query to the database will fail and user will get an exception.
h3. Change Data in Collections Only via Special Methods
When having associations, take care of this association via special method: for example, Parent class will have method addChild(Child child).
When returning the values of collections wrap them using Collections.unmodifiable to prevent accidental changes.
This is useful both to handle bidirectional associations correctly and to prevent accidental changes in hash code.
Summary1. Override hashCode() and equals() of the entities using the natural key properties of the entity.
2. Don't compare auto-generated id in hashCode().
3. Don't change the properties that participate in the hashCode() for objects stored in collections that use hash code (especially Sets or Maps). If such a change must occur, reinsert object into collection. Remember that remove must occur before the property is updated.
4. Keep the persistence tier away from the business logic as much as possible. Don't pass the persistence objects to the business at all.
5. Make your objects immutable (or semi-immutable): make all setters private, return collection values only wrapped with Collections.unmodifiable, make changes in collections only via special methods.
Recommended Reading1. Hibernate in Action (In Action series)
2. Java Persistence with Hibernate
3.The Best Software Writing I: Selected and Introduced by Joel Spolsky (v. 1)