9

IBM 建议使用 EntityManagers 的最佳实践是获取/使用/关闭。如果 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;
}

集合将始终为 null,因为当有人调用 getter 时,EntityManager 已关闭。仅当 fetch 为 EAGER 时才加载集合,但这会导致每次 SQL 连接都很慢。

所以它要么是“多线程”错误,要么是 openjpa.Multithreaded=true 这是一个不好的做法,或者它很慢,因为即使不需要集合,每次都需要 SQL 连接。有什么方法可以正确地做到这一点,所以它既可以通过 Lazy fetch 快速获取,又可以仅使用最佳实践来完成?

4

4 回答 4

3

Ok, here's my conclusion after two days research on the issue. For applications that can't rely on being deployed on a Java EE server and can't guarantee single thread access (e.g. web apps on tomcat) the best practice will be indeed to open, use and close entity managers within the scope of the method of the DAO object.

This means lazy loading will not work outside of DAO. To get around it, in methods that find one entity by id all collections need to be fetched by calling size() on the collection to trigger fetching before entity manager is closed. This will make the find method return a fully loaded object even though fetch is lazy.

For methods that return collections of objects, like search, it will be too slow to fully load each entity in the search result so results returned as is without children. Whenever one of the entities in the search result needs to be viewed, it will have to be loaded individually through that method that gets fully loaded objects.

于 2012-10-23T10:01:37.303 回答
2

好的,不使用 Java EE,您可以创建一个简单的 EntityManagers 池。我使用 StackKeyedObjectPool(来自 Apache Commons Pool)并在需要时创建新的 EntityManager。我有一个借/还接口,池会根据需要自动创建新对象。见http://commons.apache.org/pool/api-1.6/org/apache/commons/pool/impl/StackKeyedObjectPool.html

于 2012-10-22T16:43:08.020 回答
0

我创建了 GitHub 项目,在 servlet(tomcat) 容器中提供自动管理的 EntityManager 生命周期。单线程 http 请求上下文检索相同的实例,并且 EM 在 http 请求结束时自动关闭。这为 servlet 和 jsp 脚本提供了简单的抽象,而无需显式的 try-catch-finally 样板。
https://github.com/Murmur/ScopedEntityManager

于 2014-07-22T19:58:20.670 回答
0

实际上,您应该能够让 Websphere 为您注入实体管理器。

@PersistenceContext(unitName = "<whatever>")
private EntityManager em;

您需要访问数据的地方。应用程序服务器将为您处理线程问题,因为每个 bean 将只处理一个请求,并且该请求将在一个线程上。

于 2012-10-19T15:11:03.380 回答