The BigDecimal rolls into town …

Like most programmers, I am probably the laziest person I know.  That means that when I can be bothered to do something, I want to take the minimum amount of time to do it, naturally with the best possible results.  This thought occured to me the other day when working on a collection of Domain Objects that had, you guessed it, similar but slightly different implementations of the same method.  Although that is bad (See my post in “Pet Hates” about this particular problem) the main issue here was how a misunderstanding of the internal workings of the BigDecimal can really screw things up for you.

The issue in this case was a utility method that attempted to determine whether any data in the current Domain Object differed from that seen in a another Domain Object.  Simple stuff.  Not so when you consider that the DO in question used a number of closely typed fields, including the infamous BigDecimal type. 

The problem arose when the values contained in DO #1’s BigDecimal fields were compared with that in DO #2’s BigD’s.  The BigDecimal.equals() method does a sterling job of comparing the two BigD values, right down to the scale (or mantissa if you are of a mathematical bent) i.e. the number of decimal places.  So 2.00 is NOT equal to 2.0, even though it clearly is to us Humans.

Every now and then, depending on how the BigD was populated, the equals() would return a false result when to the naked eye the data should be equal in all cases.  The “every now and then” part was worked out to be dependent on whether the source DO was obtained directly out of the DB via Hibernate or hauled out of Hibernate 2nd level cache. Turns out that the scale differs when reading from the DB as opposed to pulling an existing object out of the 2nd level cache.  Although this issue was limited to our code, you can see how easily an issue like this can creep in.  Scary stuff.

So the scale variation depended on whether the object had been cached or not by Hibernate, which in turn depended on whether the VM had been restarted recently or not.  The net effect was that if the DO existed before the VM restart, all was well with the equals() evalutation.  If it was created after the VM restart, the equals() call ALWAYS failed, despite the numbers in question appearing to be equal.

To the rescue came the BigDecimal.compareTo() method, which effectively normalises the scale without enforcing any potentially damaging data truncation.  Once implemented, our system became a lot more predictable.