Hibernate::
|
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:
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:
#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:
Conceptual diagram will look like this:
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 And where did the plumbeng code go? It went to special MethodInterceptor 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: 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.
|
© Copyright 2003-2006 SourceLabs, Inc. All Rights Reserved.
411 First Ave S. Ste 403, Seattle, WA 98104 | 206.322.0099 | info@sourcelabs.com