Tuesday, July 5, 2011

Cascading Persistence in Hibernate

We have said, a number of times, that when an object is made persistent, that the objects it refers to are also made persistent. This was an oversimplification. In the mapping files for the classes, there is an attribute, cascade that lets us control how much, or how little, of a reference graph gets automatically persisted, deleted or updated. The values that it can be set to, and their meanings, are as follows:
  • none: no automatic action on the referenced object takes place.
  • save-update: automatically save or update the referenced object when the referencing object is saved or the transaction commits.
    delete: automatically delete the referenced object when delete() is called on the referencing object. Note that, if the referencing object is not deleted but merely removes its reference to the referenced object, then this option will not do anything and, potentially, a garbage (or orphan) object will be left in the database.
  • delete-orphan: automatically delete any object for whom the reference has been removed from the referencing object.
  • all: take the same actions as save-update and delete but not that of delete-orphan.
  • all-delete-orphan: take the same action as save-update, delete and delete-orphan.

Requirements for object to become Hibernate Object

A Hibernate object, suitable for mapping into a database, is a normal java bean with a number of extra requirements.
  • There must be a default constructor for the class.
  • There must be accessors and mutators for all the instance variables of the class. Actually this is overstating the requirement but is a good base rule: read the Hibernate documentation for the full details.
  • The class should implement Serializable. Strictly speaking, this is not a requirement. However, in practice you will normally want your Hibernate objects to be serializable so that they can be (potentially) migrated around a multiprocessor cluster or saved and restored across a web server reboot etc.
  • The class should have an id instance variable, usually of type Long. Again this is not a true requirement but it is recommended to use automatically generated surrogate keys and, if so, to use an instance variable called id to hold it. Certainly, alternatives are possible.
  • The mutator for the id property should be private, not public. Again not a requirement but good practice. You should never update the id property directly but rather rely on Hibernate updating it for you. In practice, it is the value of this field that Hibernate uses to decide if an object has been mapped to a database record or not. Change the property yourself and you could seriously confuse Hibernate.
  • You should decide on a business key for the object and implement the equals and hashCode methods for it.
  • You should add any extra type specific constructors (which should leave the id field null) and business rule methods you like.
  • You should not make the class final if you want to be able to use lazy loading for objects of the class.

Monday, July 4, 2011

Do not return in a finally block: return will swallow your exceptions

I was recently working on a code, which was failing without giving any exception. So I had to add lots of logging to the code, to see where is the bug. I caught nothing via this method.
So I decided to check out the module source code and yeah finally I found it while debugging. The module was correctly implementing what was required and generating the required Exception as expected.

The author, unfortunately, forgot one of the rules of the Java Language and was returning from a finally block, effectively discarding the exception that was launched just a couple of lines before.

That's amazing how many times I see people returning from a finally block without understanding what the code is intended to do.

Still not convinced?

Run this:
@Test
public void hello() {
    try {
        throw new UnsupportedOperationException();
    } finally {
        return;
    }
}

No exception is thrown. Surprised? You should not. There are plenty of variations of this theme out there. Nevertheless, if you're still surprised that the hello() method completes without throwing an UnsupportedOperationException, please read on.

try/catch/finally blocks

According to the Java Language Specification, this is a simplified vision of what happens during the execution of a finally block (you can read the normative documentation here):
  • If the finally block completes normally, then the try statement completes abruptly for reason R.
  • If the finally block complete abruptly for reason S, then the try statement completes abruptly for reason S.
As you may notice in the JLS, then, the "reason" a try statement completes with will always be affected by an abrupt completion of a finally block. Said in other words, the finally block will "decide" what happens later.

You probably though many times about what happens if an exception is thrown in a finally block, don't you? Perhaps you even tested that case: the exception that will be propagated up the stack will be the exception launched in the finally block. That's not surprising, after all, and that's why you should carefully check that the code in finally blocks don't fail without control.

So far, so good. But what happens, then, when a return statement is executed inside a finally block?

The return statement

What Java programmers often misunderstand is the very nature of a return statement. The Java Language Specification is clear about that:

The return statement always completes abruptly.

That's why exceptions launched in try or catch blocks (the "reason" of their abrupt completion) are simply ignored and not relaunched when a finally block completes abruptly with a return statement, being that the "resulting" reason of the abrupt completion of the try statement, as seen in the previous section.

Lesson learned

I personally don't see any good reason, unless you want any exception to be swallowed, to return in a finally block: it defies the very reason for try/catch blocks to exist.

Do think twice (or more...) before using this construct: chances are you're doing a favor to others and yourself if you avoid it.

Chitika