Reducing Code with Inversion of Control
Perhaps you wish to use Hibernate in a JSP application (as described in Chapter 2) but are unhappy with the copy-and-paste booking shown for managing transactions. If so, you can use anonymous inner classes to reduce bookkeeping while preserving control. This is popularly referred to as inversion of control, or IoC.
The term inversion of control refers to the notion that your code is handed to another class to actually be executed. Whenever you write code intended to run inside another application, you are, in a sense, inverting (or transferring) control of the execution environment to another system. In particular, the use in Java of anonymous inner classes in conjunction with a framework is dubbed "inversion of control."
Listing 12.1 shows the use of an anonymous inner class to actually perform the database access. Two independent operations are shown. Each operation is performed by an anonymous HibernateSessionWrapper() implementation of the task() method. The first operation demonstrates access to the database, the second is an example of error handling. Note that the code shows no exception-handling strategy; it is subsumed into the underlying HibernateSession Wrapper class.
Listing 12.1. Inversion of Control Example
package com.cascadetg.ch12;
import java.util.Iterator;
public class IoCExample
{
public static void main(String[] args)
{
// Need one global configuration
HibernateSessionWrapper.initialization();
// Create our task as an anonymous inner class.
// Note, no exception handling needed, implicit
// availability of a session.
HibernateSessionWrapper myTask = new
HibernateSessionWrapper()
{
Object task(Object in) throws Exception
{
net.sf.hibernate.Query myQuery = session
.createQuery((String) in);
Iterator result = myQuery.list().iterator();
session.flush();
return result;
}
};
// Execute a bit of HQL and get the results
Iterator result = (Iterator) myTask
.perform("from Author");
while (result.hasNext())
{
System.out.println(result.next().toString());
System.out.flush();
}
// Create our task as an anonymous inner class.
// Note, no exception handling needed, implicit
// availability of a session.
HibernateSessionWrapper myTask2 = new
HibernateSessionWrapper()
{
Object task(Object in) throws Exception
{
throw new Exception();
}
};
// Do the task.
if (myTask2.perform() == null)
System.out.println(myTask2.getException());
}
}
The anonymous inner class in Listing 12.1 is a subclass of the abstract class shown in Listing 12.2. Only the task() method must be overridden. If the task() method fails because of an exception, the method automatically returns null, and additional methods are provided to determine the precise nature of the exception.
Listing 12.2. Inversion of Control Example
package com.cascadetg.ch12;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
public abstract class HibernateSessionWrapper
{
/** This method must be overridden to actually do anything
*/
abstract Object task(Object in) throws Exception;
Session session = null;
Transaction transaction = null;
boolean stale = false;
Exception problem = null;
/**
* Used to identify that a stale object was encountered as
* part of the transaction.
*/
public boolean isStaleOperation()
{
return stale;
}
public Exception getException()
{
return problem;
}
/** We use this session factory to create our sessions */
public static SessionFactory sessionFactory;
/**
* Loads the Hibernate configuration information, sets up the
* database and the Hibernate session factory.
*
* Should be customized for your application.
*/
public static void initialization()
{
try
{
Configuration myConfiguration = new
Configuration();
myConfiguration
.addClass(com.cascadetg.ch02.Post.class);
myConfiguration
.addClass(com.cascadetg.ch02.Author.class);
// Sets up the session factory (used in the rest
// of the application).
sessionFactory = myConfiguration
.buildSessionFactory();
} catch (Exception e)
{
e.printStackTrace();
}
}
public Object perform()
{
return perform(null);
}
/**
* Actually does the set-up for the session, as well as
* execute the task. Handles all of the exceptions (you may
* wish to customize this per your login preference)
*/
public Object perform(Object in)
{
Object result = null;
try
{
session = sessionFactory.openSession();
transaction = session.beginTransaction();
result = task(in);
transaction.commit();
} catch (StaleObjectStateException staleException)
{
stale = true;
try
{
transaction.rollback();
} catch (Exception e2)
{
e2.printStackTrace();
}
} catch (Exception e)
{
result = null;
problem = e;
e.printStackTrace();
try
{
transaction.rollback();
} catch (Exception e2)
{
// Notify of a failure to roll the transaction
// back
e.printStackTrace();
}
} finally
{
try
{
if (session != null) session.close();
} catch (Exception e)
{
// Silent failure of session close
}
}
return result;
}
}
If you find the inversion of control style of development interesting, you may wish to investigate the Spring framework (http://www.springframework.org/). Spring is a more sophisticated IoC framework than the one shown in Listing 12.2; it provides a number of additional services (both specific to Hibernate and helpful for application development in general).
 |