Tuesday, April 17, 2012

Hibernate: Why cant you return Null...

In this post, I rant about a patch the application needs to take care of when the framework is flawed. In this case, the framework happens to be Hibernate. The jar version used is : 3.6.6.Final
I was working on database query (removing sub-selects) optimizations and hence the need for refactoring the code. Below is the method which I came up with for the optimization.
protected long getMaxOrderingNumberForTrackingNo(String xoomTrackingNumber){
 List<Long> orderNumberList = getHibernateTemplate().find(HQL_GET_MAX_ORDER_NUMBER_FOR_TRACKING_NUMBER, xoomTrackingNumber);
 long orderNumber=0;
 if (!CollectionUtils.isEmpty(orderNumberList)) {
  orderNumber=orderNumberList.get(0);
 }
 return orderNumber;
}
After making my changes, I quickly ran the unit and integration tests. These tests to my surprise failed and I started the quest for finding the culprit! It turns out the issue was in the  getHibernateTemplate().find() method which returned an instantiated object with size equals 1 and all elements in the list to be null. The above code hence threw a Null pointer exception. Below debugger screenshot from Idea confirms this claim.

Initially, I thought the CollectionUtils.isEmpty() method does not handle Collections.EMPTY_LIST.
Hence the method was changed to following:
protected long getMaxOrderingNumberForTrackingNo(String xoomTrackingNumber){
 List<Long> orderNumberList = getHibernateTemplate().find(HQL_GET_MAX_ORDER_NUMBER_FOR_TRACKING_NUMBER, xoomTrackingNumber);
 long orderNumber=0;
 if (orderNumberList!=null && !orderNumberList.isEmpty()) {
   orderNumber =  orderNumberList.get(0);
 }
 return orderNumber;
}

Still the test cases failed! Also my assumption about CollectionUtils.isEmpty method not handling Collections.EMPTY_LIST was wrong
Try out:
assertEquals(true, CollectionUtils.isEmpty(Collections.EMPTY_LIST));

So finally confirming that this is a flaw, did the following as a work-around, had to add in a try catch block and document this issue.

protected long getMaxOrderingNumberForTrackingNo(String xoomTrackingNumber){
 List<Long> orderNumberList = getHibernateTemplate().find(HQL_GET_MAX_ORDER_NUMBER_FOR_TRACKING_NUMBER, xoomTrackingNumber);
 long orderNumber=0;
 //Need to encapsulate below statement in a try-catch.
 //because stupid hibernate instantiates the List object with size=1 and its elements are empty.
 //This makes condition check !org.apache.commons.collections.CollectionUtils.isEmpty on the Collection useless.
 try {
   orderNumber=orderNumberList.get(0);
 } catch (NullPointerException npe) {
   orderNumber = 0;
   logger.debug("order number list is empty.");
 }
 return orderNumber;
} 

In this case, it seems to make sense for the method to return in null so that commons utils can check for its emptiness. At this point, I will consider this as a hibernate framework bug.

Found where in hibernate, it is being initialized:
In SessionImpl.java, method list(String query, QueryParameters queryParameters),  line 1263:
List results = CollectionHelper.EMPTY_LIST;

CollectionHelper's EMPTY_LIST constant is initialized as follows:
public static final List EMPTY_LIST = Collections.unmodifiableList( new ArrayList(0) );

From the screenshot above, note the size is 1 and List.isEmpty method checks on size == 0 which explains why these methods are failing! Below is the source code snippet of isEmpty method used in ArrayList.java
/**
 * Returns <tt>true</tt> if this list contains no elements.
 * @return <tt>true</tt> if this list contains no elements
 */
 public boolean isEmpty() {
  return size == 0;
 }
As expected, a quick unit test does also confirm that when a null element is added to List, the size is not 0. Following code fails on assertEquals.
List results = new ArrayList();
results.add(null);
assertEquals(0, results.size());

No comments:

Post a Comment