Monday 15 October 2012

Spring, JPA and Hibernate. How to access the Session Factory.

I am currently working on a Spring application with JPA and Hibernate. After adding cache on the relations I started to experience problems with the many-to-many relations. The reason for this is that these relations are not updated bi-directionally as shown below.

public class B {
    @ManyToMany
    private List listA;

    public void addToListA(A a) {
        this.listA.add(a);
        a.getListB().add(this);
    }

    public void removeFromListA(A a) {
        this.listA.remove(a);
        a.getListB().remove(this);
    }
}
In my case this turns out to be unpractical since I use Spring Roo to scaffold the application. In my generated controller I get the collection of new values that is assigned in one go to the corresponding property. So what to do? I choose to invalidate the collection cache for the specific object. But there is a slight catch. JPA is configured by Spring to use Hibernate. This is all done through the spring XML configuration files and there is no direct access to the hibernate SessionFactory from code. But there is a trick. Add the following bean to the applicationContext.xml file where the JPA entityManager is defined.
  
       
   

This will initialize the static fields of the following class.
package com.fennek.sessionexposer;

import java.io.Serializable;

import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.Cache;
import org.hibernate.SessionFactory;

import javax.persistence.EntityManagerFactory;


public class HibernateSessionFactoryService {
 static SessionFactory oFactory;
 
    public static SessionFactory setEntityManagerFactory(EntityManagerFactory hemf) {
     if (hemf != null){
      oFactory = ((HibernateEntityManagerFactory)hemf).getSessionFactory();
     }
     return oFactory;
    }
    
    public static SessionFactory getSessionFactory()
    {
     return oFactory;
    }
    
    public static void Evict(String entityName, Serializable id)
    {
     if (oFactory != null){
      Cache cache = oFactory.getCache();
      if (cache != null){
       cache.evictEntity(entityName, id);
      }
     }
    }
    
    public static void EvictCollection(String roleName, Serializable ownerId)
    {
     if (oFactory != null){
      Cache cache = oFactory.getCache();
      if (cache != null)
      {
       cache.evictCollection(roleName, ownerId);
      }
     }
    }
}
Now you have access to the Hibernate SessionFactory through the HibernateSessionFactoryService class. This allows you to invalidate the cache, but also gives you access to the Hibernate statistics.

4 comments: