Fabio Cepeda

Archive for September 2008

Today, I experienced what I have read in this document from Hibernate a couple of months ago. The importance of the equals() and the hashCode() methods for hibernate when working with collections in persistent objects.

The problem:

I had an object that needed a many-to-many instead of a many-to-one relationship. Since last week I had the same requirement, I thought, this should be done in no time maybe from 1 to 3 hours after updating the DB and all the code that needed to be re-written. Well, I had a tough time finding the reason for something awkward that was happening to this piece of code. I guess bugs are hard to find when they are not part of the code that you are changing directly or when you expect certain things in place.

Symptoms:

  1. If I inserted records in the join table directly the page was rendered correctly with all the collection elements. No problem here.
  2. When the form was submitted the collection elements disappear, the same behavior happened when adding a new element to the collection through jQuery
  3. When a validation error occurred and the collection had elements the form was rendered with empty collection elements.

Can you guess what was the problem? In my search of this error I double check the whole code: JSP, the controller, the mappings, the hibernate code and the POJO. Initially, I focused in the controller and the JSP because it was what I change the most. By the way, this application is based on Appfuse ant it uses SpringMVC. I noticed that the form controller was not calling the formBackingObject() after the form was submitted successfully, but I could not figure out why. Check the settings on the controller and I did not find anything unusual.

Check the hibernate mapping and everything was ok. It was ok because it showed the collections when it was added directly to the DB and it did not any hibernate errors, the entity was getting at the hibernate method with correct elements in the collection. After this two observations I knew the problem was lower in the POJO itself.

Well, finally I checked the persitent entity completely and what I found was that the POJO did not have neither equals() nor hashCode() implemented, and since this application uses OpenSessionInViewInterceptor that is one session-per-request all the awkwardness made sense.

After implementing this methods everything worked ok.

The lesson learned:

  • Always give a quick check to the basics, check for equals, hashCode and toString methods in your POJOs.
  • Hibernate uses the session to identify objects. The collection was shown correctly the first time one request one hibernate session and then session closed, but at the second request at form submission new hibernate session new request and then weird stuff happening because Hibernate was not able to tell if that object on the form was the same as the one previously fetched from the DB.
  • Hibernate uses this session to be able to move objects in and out of memory and that is way equals and hashCode are important.
  • If you want to store an object List, Map, or Set then it is a requirement to implement equals() and hashCode(), so they obey the contract from the specification.

The good thing for me is once I learned this way is really hard to forget.

These last two weeks I’ve been re-working on dynamically binding a list of objects using SpringMVC. My goal is not to loose the elements in the collections when an error happens in the submission of the form. Right now I am adding and deleting collection elements using jQuery, and DWR excellent javascript libraries by the way.

Some history of the problem:

The first attempt used what Matt Raible used initially in Appfuse 1.8 long time ago, where a select list was used to group the elements of the collection and at submission the collection was formed in the onSubmit method of the form controller. The second attempt used a new kind of editor in spring called CustomCollectionEditor and also followed some later findings of Matt Raible. In this solution, spring was able to form a collection of objects of any type by overriding the convertElement() method in the initBinder() method. This solution clear up the code in the onSubmit() method and move it where it belongs. The addition and deletion of objects in the collections is managed by javascript, but this causes to lose the elements of a collection in the presence of an error during form submission.

For right now, I have opted to avoid the binding errors through optical cues in the form so that the user can select what is suppossed to. One thing that I discover is that if you want to have the elements binded when added to the collection with javascript you will have to exposed this functionality through DWR and make sure the element gets persisted. In my case this complicate things since the collection element is a fairly complex and large POJO. Maybe there is another way, using onBind or those methods to make sure the collection is binded. I’ll check into that and post my findings later.