Reputation: 1514
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?
Upvotes: 10
Views: 5193
Reputation: 10400
I have created GitHub project providing automanaged EntityManager lifetime within servlet(tomcat) container. Single threaded http request context retrieves same instance and EM is automatically closed at the end of http request. This gives simple abstraction for servlet and jsp scripts without explicit try-catch-finally boilerplate.
https://github.com/Murmur/ScopedEntityManager
Upvotes: 0
Reputation: 1514
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.
Upvotes: 3
Reputation: 2020
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
Upvotes: 2
Reputation: 2020
Actually, you should be able to have Websphere inject the entity manager for you.
@PersistenceContext(unitName = "<whatever>")
private EntityManager em;
where you need to access the data. The application server will handle the threading issues for you, as each bean will process only a single request and that request will be on a single thread.
Upvotes: 0