在JSE中使用JPA的EntityManager ThreadLocal模式 [英] EntityManager ThreadLocal pattern with JPA in JSE

查看:174
本文介绍了在JSE中使用JPA的EntityManager ThreadLocal模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Struts 1.3 + JPA(以Hibernate作为持久性提供者)开发一个简单的Book Store项目。我无法切换到Spring或任何其他更复杂的开发环境(例如Jboss),我也不能使用任何Hibernate特有的技术(例如, Session class)。



鉴于我在JSE环境中,我需要明确地管理整个EntityManager的生命周期。



< Book 实体的定义如下:

  @Entity 
public class Book {

@Id private String isbn;
私有字符串标题;
私人日期publishDate;

// Getters and Setters
}

我定义了三个 Action 类,它们分别负责检索所有图书实例,通过ISBN检索单个图书实例并将分离的图书合并到数据库中。 b
$ b

为了增加业务逻辑代码和数据访问代码之间的关注点分离,我引入了一个简单的 BookDAO 对象,它是执行CRUD操作的费用。理想情况下,所有与数据访问相关的调用都应委派给持久层。例如, ListBookAction 定义如下:

  public class ListBookAction扩展操作{

private BookDAO dao = new BookDAO();
$ b $ @Override
public ActionForward execute(ActionMapping映射,ActionForm表单,
HttpServletRequest请求,HttpServletResponse响应)
抛出异常{

/ /检索所有书籍
列表< Book> books = dao.findAll();

//保存结果集
request.setAttribute(books,books);

//转发到视图
返回mapping.findForward(booklist);
}

}

BookDAO对象需要访问 EntityManager 实例来执行任何操作。鉴于 EntityManger 不是线程安全的,我引入了一个名为 BookUnitSession 的helper类,它将EntityManager封装在 ThreadLocal 变量:

  public class BookUnitSession {

private静态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;
}

}

一切似乎都奏效,但我仍然有一些担忧。即:


  1. 这个解决方案是最好的解决方案吗?这是这种情况下的最佳实践?

  2. 我仍然需要明确地关闭EntityManager和EntityManagerFactory。我该怎么做?

谢谢 在过去的几天里,我设计了一个可能的解决方案。我试图用 BookUnitSession 类构建的实际上是 EntityManagerHelper 类:

  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();






$ p这样的类可以确保每个线程(即每个线程请求)将获得它自己的 EntityManager 实例。因此,每个DAO对象都可以通过调用 EntityManagerHelper.getEntityManager()

$ b来获得正确的 EntityManager 实例
$ b

根据session-per-request模式,每个请求必须打开和关闭它自己的 EntityManager 实例,它将负责封装交易中所需的工作单位。这可以通过实现为 ServletFilter 的截取过滤器来完成:

  public class EntityManagerInterceptor implements Filter {

@Override
public void destroy(){}
$ b @Override
public void init(FilterConfig fc) throws ServletException {}
$ b @Override
public void doFilter(ServletRequest req,ServletResponse res,
FilterChain链)抛出IOException,ServletException {

try {
EntityManagerHelper.beginTransaction();
chain.doFilter(req,res);
EntityManagerHelper.commit(); $ EntityManagerHelper.getEntityManager()。isOpen())
EntityManagerHelper.rollback() ();
throw e;

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

这种方法也允许查看(例如,JSP页面)来获取实体的字段,即使它们已经被延迟初始化(在视图模式中打开会话)。
在JSE环境中,当servlet容器关闭时, EntityManagerFactory 需要显式关闭。这可以通过使用 ServletContextListener 对象来完成:

  public class EntityManagerFactoryListener实现ServletContextListener {

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

@Override
public void contextInitialized(ServletContextEvent e){}

}

web.xml 部署描述符:

 <听者GT; 
< description> EntityManagerFactory监听器< / description>
< listener-class> package.EntityManagerFactoryListener< / listener-class>
< / listener>

< filter>
< filter-name>拦截器< / filter-name>
< filter-class> package.EntityManagerInterceptor< / filter-class>
< / filter>

< filter-mapping>
< filter-name>拦截器< / filter-name>
< url-pattern> *。do< / url-pattern>
< / filter-mapping>


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

解决方案

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>

这篇关于在JSE中使用JPA的EntityManager ThreadLocal模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆