Hibernate issues

Last week I got a chance to use my new Hibernate materials.  Fortunately, since I was there to deal with them, they weren’t the major problem they should have been.  I understand this stuff a lot better now than when I wrote them, which I suppose is what a beta stage is all about.

(Or not.  I’d say the materials were probably more in an alpha stage.  You’re not supposed to add completely new functionality in the beta phase.)

There are, as it turns out, three important pages on the Hibernate Wiki that every Hibernate developer should read.

  1. Sessions and transactions
  2. Generic Data Access Objects
  3. Open Session in View

The Hibernate team obviously believes these are important, too, as they are all linked to each other.

The page on sessions and transactions explains that the SessionFactory.openSession method is, for all practical purposes, deprecated.  Okay, maybe that’s too harsh, but they continually refer to the “session per operation” anti-pattern, which is a pretty strong statement.

Instead, the preferred mechanism is to use SessionFactory.getCurrentSession, which of course relies on there being a current session in the first place.  One of my primary references for Hibernate, Pro Hibernate 3.0 by Minter and Linwood, demonstrates using a ThreadLocal object to store a session in a stand-alone application.  This turns out to be a sufficiently important mechanism that its actually built into the distribution.

All that is necessary is to go into the Hibernate configuration file (either hibernate.cfg.xml or hibernate.properties) and set two values:


hibernate.transaction.factory_class --> org.hibernate.transaction.JDBCTransactionFActory

and

hibernate.current_session_context_class --> thread

It turns out that the JDBCTransactionFactory is the default, so really only the second value needs to be set.  Doing so enables the code to use getCurrentSession everywhere, which also means the call to session.close in a finally block is also eliminated.

Of course, if an application uses JTA, the approach is somewhat different, but equally simple.  See the link for details.

The Open Session in View page implements this as the session per request mechanism.  They recommend using a servlet filter to get the current session, begin a transaction, and call the next filter in the chain.  When the response makes its way back, then call commit or rollback as necessary.

That’s easy enough to do.  Since a servlet filter implies a servlet container (i.e., an application server), you can also use JTA and JNDI lookups for the SessionFactory singleton.

Finally, we’ve got the generic DAO pattern.  I can totally understand the need for that, since creating DAOs can involve a lot of repetitive code.  Normally when I make a DAO I add in all the CRUD methods (create, retrieve, update, delete) in terms of the actual class.  For example, I would have a UserDAO class that would look like

public interface UserDAO {
  void addUser(User u);
  User findUserById(Integer id);
  List<User> findAllUsers();
  void updateUser(User u);
  void deleteUser(User u);
}

I might add overloads for id arguments, but that’s the basic structure.  The problem is, adding that for all entities is a pain, not to mention that the implementation classes are all very, very similar.

The generic DAO presented on the wiki page uses Java 5 generics to build a simpler approach which can be extended easily.  The methods they use are:

public interface GenericDAO(T, ID extends Serializable) {
  T findById(ID id, boolean lock);
  List<T> findAll();
  List<T> findByExample(T exampleInstance);
  T makePersistent(T entity);
  void makeTransient(T entity);
}

I like the makePersistent and makeTransient methods.  They replace any save, update, or delete methods in my DAOs.  When you look at their implementation of this interface in terms of an abstract class, makePersistent calls saveOrUpdate, makeTransient calls delete, and findById calls load.

(I don’t know why they call load rather than get.  The get method does the same thing, but if the entity doesn’t exist in the database then get returns null while load throws an exception.  I guess they want to throw an exception.  That’s easy enough to change.)

The other find methods actually call a protected method called findByCriteria(Criterion…).  It looks like if I want to have a findByName(String) method in my UserDAO, then when I extend GenericHibernateDAO and call findByCriteria with my own Restriction.eq(“name”,name) or some such.

Anyway, it’s a very interesting, reusable approach and simplifies the creation of DAOs enormously.  After all, if there are no special business related methods other than the CRUD methods, just extend the abstract class and you’re done.

One aside, though.  Acquiring a session is one of those “cross-cutting concerns” as Spring would say.  The DAOs shouldn’t care where or how a session is acquired, but they do need one in order to call load, saveOrUpdate, etc.  Therefore, the GenericHibernateDAO class adds in the setSession and getSession methods, which are invoked by a client somewhere.  That leads to a discussion of factories, or even dependency injection.

Hmm.  Lots to learn and lots to know.  Still, this seems to be the way the industry is going, so it’s all worth it either directly or as a collection of best practices.

I just wish all app servers (that means you, WebSphere) understood Java 5 generics.  While the syntax is ugly and awkward, the gain is too great to ignore, especially in cases like this.

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

2 Responses to Hibernate issues

  1. Tim Nesham says:

    It’s interesting that you say (paraphrasing), “I don’t know why they call load rather than get, they do the same thing except load throws an exception and get returns null, if the record is not found”. I’ve seen the difference between get and load explained that way before. However, that is not my experience, and I’m certain there’s more to it, but I cannot find it specifically. In my experience, I was playing with JSF and chapter 7 (Spring) from Hibernate Quickly (Manning, Peak). After getting Hibernate/Spring/JSF to work together, I received the exception:
    “LazyInitializationException: could not initialize proxy – the owning Session was closed”
    I don’t need to get into a lengthy discussion of sessions, etc. However, I ran into a comment that said to change the ‘load’ to a ‘get’. I did and it worked. Now, if the difference is related to not being able to locate the record, then get should have returned ‘null’, no? But get returned the record and load threw up. Makes me think there’s more to it or am I missing something?

  2. kousenit says:

    A LazyInstantiationException is supposed to occur when an association was followed but not originally fetched and the Session is closed. I agree that load will throw an exception in that case. But you’re saying that the get method actually returned the record (or, presumably the object it mapped to) rather than null?

    All I can think of is that the resulting object must have been cached somewhere. If load tried to follow the association and lazy was true (the default), that’ll throw an exception. Perhaps the get call returned the object from the cache, though, rather than null.

    I looked at that book when I was doing my initial reading on Hibernate and wasn’t really wild about it. I’ll look again, though.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: