EntityManager ThreadLocal pattern with JPA in JSE
Asked Answered
D

2

15

I'm developing a simple "Book Store" project using Struts 1.3 + JPA (with Hibernate as persistence provider). I cannot switch to Spring or any other more sophisticated development environment (e.g., Jboss) and I cannot use any Hibernate-specific technique (e.g., Session class).

Given the fact that I'm in a JSE Environment, I need to explicitly manage the whole EntityManager's lifecycle.

The Book entity is defined as follows:

@Entity
public class Book {

@Id private String isbn;
private String title;
private Date publishDate;

    // Getters and Setters
}

I defined three Action classes, which are responsible, respectively, of retrieving all book instances, retrieving a single book instance by its ISBN and merging a detached book into the DB.

In order to increase separation of concerns between business-logic code and data-access code, I introduced a simple BookDAO object, which is charge of executing CRUD operations. Ideally, all data-access related calls should be delegated to the persistence layer. For example, the ListBookAction is defined as follows:

public class ListBookAction extends Action {

    private BookDAO dao = new BookDAO();

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        // Retrieve all the books
        List<Book> books = dao.findAll();

        // Save the result set
        request.setAttribute("books", books);

        // Forward to the view
        return mapping.findForward("booklist");
    }

}

The BookDAO object needs to access an EntityManager instance in order to do any operation. Given that EntityManger is not thread-safe, I introduced an helper class named BookUnitSession which encapsulates EntityManager within a ThreadLocal variable:

public class BookUnitSession {

    private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("BookStoreUnit");
    private static final ThreadLocal<EntityManager> tl = new ThreadLocal<EntityManager>();

    public static EntityManager getEntityManager() {
        EntityManager em = tl.get();

        if (em == null) {
            em = emf.createEntityManager();
            tl.set(em);
        }
        return em;
    }

}

Everything seems to work, but I still have some concerns. Namely:

  1. Is this solution the best thing to do? which is the best practice in this case?
  2. I still need to explictly close both the EntityManager and the EntityManagerFactory. How can I do that?

Thanks

Deadradeadweight answered 25/2, 2013 at 16:11 Comment(0)
D
26

During the last few days I designed a possible solution. What I was trying to construct with the BookUnitSession class was actually the EntityManagerHelper class:

public class EntityManagerHelper {

    private static final EntityManagerFactory emf; 
    private static final ThreadLocal<EntityManager> threadLocal;

    static {
        emf = Persistence.createEntityManagerFactory("BookStoreUnit");      
        threadLocal = new ThreadLocal<EntityManager>();
    }

    public static EntityManager getEntityManager() {
        EntityManager em = threadLocal.get();

        if (em == null) {
            em = emf.createEntityManager();
            threadLocal.set(em);
        }
        return em;
    }

    public static void closeEntityManager() {
        EntityManager em = threadLocal.get();
        if (em != null) {
            em.close();
            threadLocal.set(null);
        }
    }

    public static void closeEntityManagerFactory() {
        emf.close();
    }

    public static void beginTransaction() {
        getEntityManager().getTransaction().begin();
    }

    public static void rollback() {
        getEntityManager().getTransaction().rollback();
    }

    public static void commit() {
        getEntityManager().getTransaction().commit();
    } 
}

Such a class ensures that each thread (i.e., each request) will get its own EntityManager instance. Consequently, each DAO object can obtain the correct EntityManager instance by calling EntityManagerHelper.getEntityManager()

According to the session-per-request pattern each request must open and close its own EntityManager instance, which will be in charge of encapsulating the required unit of work within a transaction. This can be done by means of an intercepting filter implemented as a ServletFilter:

public class EntityManagerInterceptor implements Filter {

    @Override
    public void destroy() {}

    @Override
    public void init(FilterConfig fc) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {

            try {
                EntityManagerHelper.beginTransaction();
                chain.doFilter(req, res);
                EntityManagerHelper.commit();
            } catch (RuntimeException e) {

                if ( EntityManagerHelper.getEntityManager() != null && EntityManagerHelper.getEntityManager().isOpen()) 
                    EntityManagerHelper.rollback();
                throw e;

            } finally {
                EntityManagerHelper.closeEntityManager();
            }
    }
}

This approach also allows the View (e.g., a JSP page) to fetch entity's fields even if they have been lazy initialized (Open Session in View pattern). In a JSE environment the EntityManagerFactory needs to be explicitly closed when the servlet container is shutdown. This can be done by using a ServletContextListener object:

public class EntityManagerFactoryListener implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent e) {
        EntityManagerHelper.closeEntityManagerFactory();
    }

    @Override
    public void contextInitialized(ServletContextEvent e) {}

}

The web.xml deployment descriptor:

<listener>
  <description>EntityManagerFactory Listener</description>
  <listener-class>package.EntityManagerFactoryListener</listener-class>
</listener>

<filter>
  <filter-name>interceptor</filter-name>
  <filter-class>package.EntityManagerInterceptor</filter-class>
</filter>

<filter-mapping>
  <filter-name>interceptor</filter-name>
  <url-pattern>*.do</url-pattern>
</filter-mapping>
Deadradeadweight answered 1/3, 2013 at 14:15 Comment(8)
Thank you. I'm currently using Tomcat 7 and Struts 1.3 without any application server. Therefore, I'd say I'm not completely JEE compliant. From the view point of persistency, it is like being in a JSE environment. That's why I stated JSE in the title.Deadradeadweight
Hi, did this solution work out in the end? I heard about some issues with the ThreadLocal usage.Omnipotence
Hi, this worked out in the end, even if I had to apply some minor changes to the EntityManagerInterceptor class. To what issues are you referring?Deadradeadweight
burton0: I've had memleaks with ThreadLocal's (Tomcat gave me warnings); my solution was to add a @WebListener implementing the ServletRequestListener interface; and in the requestDestroyed() method do the cleanup i.e. threadLocal.remove(). Also, a favor. I'm currently in a Spring project, and I desperately need to get rid of Spring. Would you mind sharing the final version of your EntityManagerInterceptor? Perhaps in Google Code, or something? I'd really appreciate it. For non-Spring and non-EJB users, this is a very needed example. Thanks!Hospodar
I implemented your method, but realized the code inside doFiler() was being executed several times per request. Is that intended?Winnebago
Hi. Not really, the code should be executed once per request.Deadradeadweight
I mapped the filter to xhtml pages. I am using facelets and templating, including other pages before serving. Could that be the reason?Winnebago
I'm not familiar with facelets. As a general principle, any single request will get bound to its own EntityManager. Hence, you should try to understand how many requests are triggered.Deadradeadweight
N
0

ScopedEntityManager helper tool I have created in Github uses a similar technique. Instead of request filter I have chosen ServletRequestListener for lifecycle management. Also I am not using a threadlocal because they have a habit of memory leaks in J2EE containers if not programmed carefully. Tomcat do have some tricks to failsafe certain human errors.

Necrotomy answered 3/7, 2015 at 5:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.