在JSE中使用JPA的EntityManager ThreadLocal模式
我正在使用Struts 1.3 + JPA(使用Hibernate作为持久性提供程序)开发一个简单的“ Book
Store”项目。我不能切换到Spring或任何其他更复杂的开发环境(例如Jboss),也不能使用任何特定于Hibernate的技术(例如,Session类)。
考虑到我处于JSE环境中,我需要显式管理整个EntityManager的生命周期。
该Book实体被定义如下:
@Entitypublic class Book {
@Id private String isbn;
private String title;
private Date publishDate;
    // Getters and Setters
}
我定义了三个Action类,分别负责检索所有书籍实例,通过其ISBN检索单个书籍实例并将独立的书籍合并到数据库中。
为了增加业务逻辑代码和数据访问代码之间的关注点分离,我引入了一个简单的BookDAO对象,负责执行CRUD操作。理想情况下,所有与数据访问相关的调用都应委派给持久层。例如,ListBookAction定义如下:
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");
    }
}
BookDAO对象需要访问EntityManager实例才能执行任何操作。鉴于这EntityManger不是线程安全的,我引入了一个名为helper的类,该类BookUnitSession将EntityManager封装在一个ThreadLocal变量中:
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;
    }
}
一切似乎正常,但是我仍然有一些担忧。即:
- 此解决方案是最好的办法吗?在这种情况下,哪种最佳做法是?
 - 我仍然需要明确关闭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();
    } 
}
这样的类确保每个线程(即每个请求)将获得其自己的EntityManager实例。因此,每个DAO对象可以EntityManager通过调用获得正确的实例EntityManagerHelper.getEntityManager()
根据每次请求会话模式,每个请求必须打开和关闭其自己的EntityManager实例,该实例负责将事务中所需的工作单元封装起来。这可以通过实现为的拦截过滤器来完成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();
            }
    }
}
这种方法还允许View(例如,JSP页面)获取实体的字段,即使它们已经被延迟初始化(View模式中的Open
Session)。在JSE环境中,EntityManagerFactory当servlet容器关闭时,需要显式关闭。这可以通过使用一个ServletContextListener对象来完成:
public class EntityManagerFactoryListener implements ServletContextListener {    @Override
    public void contextDestroyed(ServletContextEvent e) {
        EntityManagerHelper.closeEntityManagerFactory();
    }
    @Override
    public void contextInitialized(ServletContextEvent e) {}
}
在web.xml部署描述符:
<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模式 的全部内容, 来源链接: utcz.com/qa/402538.html
