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.
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.
Leave a Reply