在JSE中使用JPA的EntityManager ThreadLocal模式

我正在使用Struts 1.3 + JPA(使用Hibernate作为持久性提供程序)开发一个简单的“ Book

Store”项目。我不能切换到Spring或任何其他更复杂的开发环境(例如Jboss),也不能使用任何特定于Hibernate的技术(例如,Session类)。

考虑到我处于JSE环境中,我需要显式管理整个EntityManager的生命周期。

Book实体被定义如下:

@Entity

public 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;

}

}

一切似乎正常,但是我仍然有一些担忧。即:

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

}

}

这样的类确保每个线程(即每个请求)将获得其自己的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

回到顶部