Hibernate unit of work

 Unit of work

First, let's define a unit of work. A unit of work is a design pattern described by Martin Fowler as “[maintaining] a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. ”[PoEAA] In other words, its a series of operations we wish to carry out against the database together. Basically, it is a transaction, though fulfilling a unit of work will often span multiple physical database transactions. So really we are talking about a more abstract notion of a transaction. The term "business transaction" is also sometimes used in lieu of unit of work.
do not open and close a Session for every simple database call in a single thread. The same is true for database transactions. Database calls in an application are made using a planned sequence; they are grouped into atomic units of work. This also means that auto-commit after every single SQL statement is useless in an application as this mode is intended for ad-hoc SQL console work. Hibernate disables, or expects the application server to disable, auto-commit mode immediately. Database transactions are never optional. All communication with a database has to occur inside a transaction. Auto-commit behavior for reading data should be avoided, as many small transactions are unlikely to perform better than one clearly defined unit of work. The latter is also more maintainable and extensible.
Your application code can access a "current session" to process the request by calling sessionFactory.getCurrentSession(). You will always get a Session scoped to the current database transaction.
Startup and helpers

It's time to load and store some Event objects, but first we have to complete the setup with some infrastructure code. We have to startup Hibernate. This startup includes building a global SessionFactory object and to store it somewhere for easy access in application code. A SessionFactory can open up new Session's. A Session represents a single-threaded unit of work, the SessionFactory is a thread-safe global object, instantiated once.

        We'll create a HibernateUtil helper class which takes care of startup and makes accessing a SessionFactory convenient. Let's have a look at the implementation:

package util;

import org.hibernate.*;
import org.hibernate.cfg.*;

public class HibernateUtil {

    private static final SessionFactory sessionFactory;

    static {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

}


Loading and storing objects

Finally, we can use Hibernate to load and store objects. We write an EventManager class with a main() method:

package events;
import org.hibernate.Session;
 
import java.util.Date;
 
import util.HibernateUtil;
 
public class EventManager {
 
    public static void main(String[] args) {
        EventManager mgr = new EventManager();
 
        if (args[0].equals("store")) {
            mgr.createAndStoreEvent("My Event", new Date());
        }
 
        HibernateUtil.getSessionFactory().close();
    }
 
    private void createAndStoreEvent(String title, Date theDate) {
 
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
 
        session.beginTransaction();
 
        Event theEvent = new Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);
 
        session.save(theEvent);
 
        session.getTransaction().commit();
    }
 
}

We create a new Event object, and hand it over to Hibernate. Hibernate now takes care of the SQL and executes INSERTs on the database. Let's have a look at the Session and Transaction-handling code before we run this.

A Session is a single unit of work. For now we'll keep things simple and assume a one-to-one granularity between a Hibernate Session and a database transaction. To shield our code from the actual underlying transaction system (in this case plain JDBC, but it could also run with JTA) we use the Transaction API that is available on the Hibernate Session.

What does sessionFactory.getCurrentSession() do? First, you can call it as many times and anywhere you like, once you get hold of your SessionFactory (easy thanks to HibernateUtil). ThegetCurrentSession() method always returns the "current" unit of work. Remember that we switched the configuration option for this mechanism to "thread" in hibernate.cfg.xml? Hence, the current unit of work is bound to the current Java thread that executes our application. However, this is not the full picture, you also have to consider scope, when a unit of work begins and when it ends.

A Session begins when it is first needed, when the first call to getCurrentSession() is made. It is then bound by Hibernate to the current thread. When the transaction ends, either through commit or rollback, Hibernate automatically unbinds the Session from the thread and closes it for you. If you call getCurrentSession() again, you get a new Session and can start a new unit of work. This thread-bound programming model is the most popular way of using Hibernate, as it allows flexible layering of your code

Related to the unit of work scope, should the Hibernate Session be used to execute one or several database operations? The above example uses one Session for one operation. This is pure coincidence, the example is just not complex enough to show any other approach. The scope of a Hibernate Session is flexible but you should never design your application to use a new Hibernate Session for every database operation. So even if you see it a few more times in the following (very trivial) examples, consider session-per-operation an anti-pattern.

1.4.1. Writing the basic servlet

Create a new class in your source directory, in the events package:

package events;
 
// Imports
 
public class EventManagerServlet extends HttpServlet {
 
    // Servlet code
}

protected void doGet(HttpServletRequest request,
                     HttpServletResponse response)
        throws ServletException, IOException {
 
    SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
 
    try {
        // Begin unit of work
        HibernateUtil.getSessionFactory()
                .getCurrentSession().beginTransaction();
 
        // Process request and render page...
 
        // End unit of work
        HibernateUtil.getSessionFactory()
                .getCurrentSession().getTransaction().commit();
 
    } catch (Exception ex) {
        HibernateUtil.getSessionFactory()
                .getCurrentSession().getTransaction().rollback();
        throw new ServletException(ex);
    }
 
}

The pattern we are applying here is called session-per-request. When a request hits the servlet, a new Hibernate Session is opened through the first call to getCurrentSession() on theSessionFactory. Then a database transaction is started—all data access as to occur inside a transaction, no matter if data is read or written (we don't use the auto-commit mode in applications).

Do not use a new Hibernate Session for every database operation. Use one Hibernate Session that is scoped to the whole request. Use getCurrentSession(), so that it is automatically bound to the current Java thread.

Next, the possible actions of the request are processed and the response HTML is rendered. We'll get to that part soon.

Finally, the unit of work ends when processing and rendering is complete. If any problem occured during processing or rendering, an exception will be thrown and the database transaction rolled back. This completes the session-per-request pattern. Instead of the transaction demarcation code in every servlet you could also write a servlet filter. See the Hibernate website and Wiki for more information about this pattern, called Open Session in View—you'll need it as soon as you consider rendering your view in JSP, not in a servlet.

Example

/** 
Called before the processing of the request is passed off to the Controller.
*/
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {

    Session sess = sessionFactory.openSession();

    sess.beginTransaction();

    ManagedSessionContext.bind((org.hibernate.classic.Session)sess);

    return true;

}
/**Called after the view has been processed.*/ 
public void afterCompletion(HttpServletRequest request, 

HttpServletResponse response, Object handler, 

 Exception ex) throws Exception {

    Session sess = sessionFactory.getCurrentSession();

    sess.getTransaction().commit();

    sess.close();

    ManagedSessionContext.unbind(this.sessionFactory);
}

Comments

Popular posts from this blog

SinglePass Terms of Service

Jasper Report Viruatization

Generate reports dynamically using Jasper Reports without generating a jasper for each report