Hibernate::
beyond "hello world"

 


Download sources

Contact us::

Hibernate Context and Transaction propagation.

Last update: Jan-08-2005

We know that Hibernate operates in certain context, or we can say it creates own context in the form of Hibernate session. The classical approach is to create Hibernate Session - > Start Transaction-> do our business - > Finalize (Commit or Rollback ). This makes perfect sense, but upon closer examination of our code we can notice that it is filled with try-catch statements. It makes it similar to JDBC code with the same problems:

  • obscurity of business logic. Sure Hibernate requires just one line of code to save an object, but we have to surround it by many lines of plumbing code like this:
    Session s;
    try{
      s = getSession();
      s.beginTransaction();
      s.save( o);
      s.getTransaction().commit();
    }catch(Exception e ){
      s.getTransaction().rollback();
    }finally{
    s.close();
    }
    as we may see there is just one line of application logic and about 8 lines of plumbing. In other words only 15% of code expresses business logic.
  • error prone repetitive code, it is easy to forget to close session or transaction properly.
  • as we have such code all over the place we start to wander what to do if we call

Can we address this somehow? Sure! For the beginning lets define our goal: which is to minimize amount of plumbing code, ideally it would look like this:

session.save( o );

And something will provide us with Hibernate session and properly commit or rollback transaction and close session. We could employ AOP techniques to this trick. We could think about some kind of wrapper for our code that will take care of plumbing. We could create a subclass of our class and so we could have all the plumbing in the subclass, and because subclass will expose the same interface as our original class we do not have to know anything particular about that subclass, except that it does its job. Or we can code to interface, and both classes: our logic implementor class and wrapper will implement the same DAO interface. There are few practical options:

  1. Use a complete AOP framework like AspectJ to do the job. This might be a bit scary, and clearly too much for simple use cases and scenarios.
  2. Use Spring framework. (We advise do not use seemingly convenient HibernateTemplate, it is better to use session per transaction approach, there is good article that describes the approach and more advanced techniques)
  3. Use HiveMind framework with HiveTranse utilities;
  4. Write something custom.

#4 might be an appealing option if adding quite big frameworks to development and production environment is not an option by any reasons. Lackily there is everything we need to build a convenient and tiny transaction and Hibernate context propagation framework is already in place. Hibernate relies on CGLib for runtime code weaving and we can employ this nice library to build our simplistic AOP framework. There are few assumption:

  • We will use transaction per request strategy (see);
  • We will code our application logic in the business logic layer classes, POJO analogs of SessionBeans;
  • For simplicity we will assume that every public method declared in such classes is the transactional method;
  • We will use a factory to create enhanced singleton instances;

Conceptual diagram will look like this:

runtime AOP

We will code our business logic classes with full knowledge of the fact that they will be enhanced and that 'something' will prowide Hibernate session for them. So the typical code will look like this

12     public void saveObject( SomeType o){
13
CallContext.getSession().save( o ); 14 }

And where did the plumbeng code go? It went to special MethodInterceptor

HibernateMethodInterceptor.java
public class HibernateMethodInterceptor implements MethodInterceptor { 
18    
19     public Object intercept(Object o, Method method, Object[] parameters, MethodProxy methodProxy) 
              throws Throwable { 
20       boolean close = true; 
21       Transaction tx = null; 
22       Object res; 
23       try { 
24         if (CallContext.getSession() == null) { 
25           Session s = SessionsFactory.createtSession(); 
26           tx = s.beginTransaction(); 
27           System.out.println("Creating H context for method " + method); 
28           CallContext.hibernateSession.set(s); 
29         } else { 
30           System.out.println("propagating H context for method " + method); 
31           close = false; 
32         } 
33    
34         res = methodProxy.invokeSuper(o, parameters); 
35    
36         if (close) { 
37           try { 
38             if (tx != null) { 
39               System.out.println("Commiting transaction"); 
40               tx.commit(); 
41               tx = null; 
42             } 
43             if (CallContext.getSession() != null) { 
44               CallContext.getSession().close(); 
45             } 
46           } finally { 
47             CallContext.hibernateSession.set(null); 
48           } 
49         } 
50         return res; 
51       } catch (Throwable e) { 
52         try { 
53           Session session = CallContext.getSession(); 
54           if (session != null ) { 
55             System.out.println("Rolling back transaction"); 
56             if (tx != null  && session.isOpen()) {             
57               tx.rollback(); 
58               tx = null; 
59             } 
60             CallContext.getSession().close(); 
61           } 
62         } finally { 
63           CallContext.hibernateSession.set(null); 
64         } 
65         throw e; 
66       } 
67    
68     } 
69   }

The line #34 is the place where our code gets invoked. Our logic gets injected into runtime subclass instance of our class by Enhancement factory, that produces singleton instances of necessary classes. The factory might look like this:

DAOSingletonsFactory.java
public class DAOSingletonsFactory { 
18    
19       private static Map enhancers = new Hashtable(); 
20       private static MethodInterceptor[] callbacks = new MethodInterceptor[]{ 
21               new EmptyMethodInterceptor(), 
22               new HibernateMethodInterceptor()}; 
23    
24       private static CallbackFilter cf = new CallbackFilter() { 
25           public int accept(Method method) { 
26               if (Modifier.isPublic(method.getModifiers()) 
                      && (!Object.class.equals(method.getDeclaringClass()))) { 
27                   System.out.println("accepting " + method); 
28                   return 1; 
29               } 
30               return 0; 
31           } 
32       }; 
33    
34    
35       public static synchronized Object getEnhancedSingletonOfClass(Class clazz) { 
36           Enhancer en = (Enhancer) enhancers.get(clazz.getName()); 
37           if (en == null) { 
38               System.out.println("Create Enhancer for class::" + clazz.getName()); 
39               en = new Enhancer(); 
40               en.setSuperclass(clazz); 
41               en.setCallbacks(callbacks); 
42               en.setCallbackFilter(cf); 
43               enhancers.put(clazz.getName(), en); 
44           } 
45           return en.create(); 
46       } 
47    
48   } 
49   

This is very simplistic AOP based Hibernate and transaction context propagation approach illustrates basic principles of session per request use case scenario. It is not silver bullet however, there might be many others, but this particular approach works very well for all kinds of environments, not just web application: standalone application, web services, etc.

Note: this approach works best if lazy object instantiation is not used or all lazy properties are used/initialized within our business method.