IBM建议使用EntityManagers的最佳做法是get / use / close。如果EntityManager未关闭,则多个线程可能会使用相同的EntityManager,这将导致以下错误:
<openjpa-2.1.2-SNAPSHOT-r422266:1179900 fatal general error> org.apache.openjpa.persistence.PersistenceException: Multiple concurrent threads attempted to access a single broker. By default brokers are not thread safe; if you require and/or intend a broker to be accessed by more than one thread, set the openjpa.Multithreaded property to true to override the default behavior.
如果加载一个将OneToMany集合映射为fetch = LAZY的对象,如下所示:
public T find(Object id) {
T t = null;
EntityManager em = getEm();
t = em.find(type, id);
em.close();
return t;
}
EntityManager getEm() {
if(this.em ==null || !this.em.isOpen()) {
this.em = emf.createEntityManager();
}
return this.em;
}
当某人调用getter时,该集合将始终为null,EntityManager将关闭。仅当fetch为EAGER时才会加载该集合,但每次都会导致SQL连接缓慢。
所以它是“多线程”错误,或者是openjpa.Multithreaded = true这是一种不好的做法,或者因为SQL连接每次都很慢,即使不需要集合也是如此。有没有办法正确地做到这一点,所以它既快速使用Lazy fetch,又只使用最佳实践?
答案 0 :(得分:3)
好的,经过两天的研究,这是我的结论。对于不能依赖于部署在Java EE服务器上且无法保证单线程访问的应用程序(例如tomcat上的Web应用程序),最佳做法是在该方法的范围内打开,使用和关闭实体管理器DAO对象。
这意味着延迟加载在DAO之外不起作用。为了解决这个问题,在通过id找到一个实体的方法中,需要通过调用集合上的size()来获取所有集合,以在实体管理器关闭之前触发提取。这将使find方法返回一个完全加载的对象,即使fetch是惰性的。
对于返回对象集合的方法(如搜索),在搜索结果中完全加载每个实体的速度太慢,因此返回的结果没有子节点。每当需要查看搜索结果中的某个实体时,就必须通过获取完全加载对象的方法单独加载它。
答案 1 :(得分:2)
好的,不使用Java EE,您可以创建一个简单的EntityManagers池。我使用StackKeyedObjectPool(来自Apache Commons Pool)并在需要时创建新的EntityManagers。我有一个借用/返回接口,并且池会根据需要自动创建新对象。见http://commons.apache.org/pool/api-1.6/org/apache/commons/pool/impl/StackKeyedObjectPool.html
答案 2 :(得分:0)
实际上,您应该可以让Websphere为您注入实体管理器。
@PersistenceContext(unitName = "<whatever>")
private EntityManager em;
您需要访问数据的位置。应用程序服务器将为您处理线程问题,因为每个bean只处理一个请求,并且该请求将在单个线程上。
答案 3 :(得分:0)
我创建了GitHub项目,在servlet(tomcat)容器中提供自动管理的EntityManager生命周期。单线程http请求上下文检索相同的实例,并且在http请求结束时EM自动关闭。这为servlet和jsp脚本提供了简单的抽象,没有显式的try-catch-finally样板。
https://github.com/Murmur/ScopedEntityManager