EntityManager ThreadLocal pattern with JPA in JSE

纵然是瞬间 提交于 2019-11-28 21:32:49

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>

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!