Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA: get-use-close of EntityManager and lazy loading

IBM suggests that the best practice for using EntityManagers is get/use/close. If EntityManager is not closed, there's a chance that the same EntityManager may be used by more than one thread which will result in the following error:

<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. 

If you load an object which has OneToMany collection mapped as fetch=LAZY like this:

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

The collection will always be null as by the time someone calls the getter, EntityManager is closed. The collection is only loaded if fetch is EAGER but that results in a SQL join every single time which is slow.

So it's either "Multiple threads" error, or openjpa.Multithreaded=true which is a bad practice or it's slow because of the SQL join every single time even if the collection is not needed. Is there any way to do it properly so it's both fast with Lazy fetch and done using best practices only?

like image 713
Maxim Suponya Avatar asked Oct 19 '12 06:10

Maxim Suponya


2 Answers

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.

like image 68
Maxim Suponya Avatar answered Nov 19 '22 09:11

Maxim Suponya


ok, without using Java EE, you can create a simple pool of EntityManagers. I use a StackKeyedObjectPool (from Apache Commons Pool) and create new EntityManagers when I need them. I have a borrow/return interface, and the pool automatically creates new object as necessary. See http://commons.apache.org/pool/api-1.6/org/apache/commons/pool/impl/StackKeyedObjectPool.html

like image 2
Zagrev Avatar answered Nov 19 '22 11:11

Zagrev