Monday, July 28, 2014

Maintaining a Long Session in Hibernate

Sometimes we need to maintain a long running session. For example, we need to do some intermediate calculation and keep track of them. The user might at the end chose to discard it or retain it. For the same purpose, we might want to keep a long running session in the Hibernate. Another way is to keep a long running transaction, but many times the transaction times out because of the underlying database. Also be careful about the issues of entities being updated by other threads, which are more about versioning issues. This can lead to commit getting failed at the end, if versioning is enabled for the entities.
The way to handle this is usually to open a hibernate session at the start of workflow and then keep the hibernate session in the Http session object. Whenever next requests comes up, the hibernate
session is tied to the current thread and is used by all the entities related calls. At the end, one can choose to commit the changes or discard it.
To start with, assume that you have identified that we are in the context of a workflow . So that means we need to create a new session or if the session is already created then bind to it.

//Let's say we do it in filter and based on certain attribute we know 
//we are in Workflow. WorkflowHandler 
//can be an interface, which can be used to designate the wrokflow related things

if (/*In the context of workflow */) {
   HttpSession httpSession = //get the session
   //check if we have already have the entity manager created
   EntityManager entityManager = 
      (EntityManager) httpSession.getAttribute("EXTENDED_PERSISTENCE_CONTEXT");
   if (entityManager != null) {
       //put this in the current thread
       } else {
            //create a new persistence context
           //emf is entity manager factory, which can be injected. Also
           //make sure to make the flush mode as manual
           EntityManager em = emf.createEntityManager();
           ((Session) em.getDelegate()).setFlushMode(FlushMode.MANUAL);
       }
    }

Also when the request is processed and just before returning back to client


 if (/* in context of workflow */) {
   HttpSession httpSession =//get session
   EntityManager entityManager = //get the entity manger
   //If the entity manager is open, then the end condition has not reached, 
   //so save it in session
   if (entityManager != null && entityManager.isOpen()) {
     ManagedSessionContext.unbind
             (((Session) entityManager.getDelegate()).getSessionFactory());
     httpSession.setAttribute("EXTENDED_PERSISTENCE_CONTEXT",
      <pass the instance of persistence context>);
     } else {
    //If we are at end, then remove the attribute
        httpSession.removeAttribute("EXTENDED_PERSISTENCE_CONTEXT");
     }
  }
Then in the intermediate step of operations, open a transaction and do the work.
EntityTransaction tx = entityManager.getTransaction();
tx.begin();        
Session session = (Session) entityManager.getDelegate();

//Do the work here.  
tx.commit();

If all the changes has to be committed at the end, then do
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
entityManager.flush();
tx.commit();
entityManager.close();
entityManager
And if it needs to be rolled back
entityManager.clear();
entityManager.close();

More write-ups on Hibernate

No comments:

Post a Comment