Issue
We use Hibernate through JPA and Spring to manage object persistence in our web application. We use open-session-in-view pattern to create sessions for threads responding to http requests. We also use some threads that are not generating views - they just wake up from time to time to do their job. That generates problems because they don't have session opened by default so they generate exceptions like
org.hibernate.SessionException: Session is closed!
or
could not initialize proxy - no Session
We found out that if every background thread invokes its logic in a method annotated with @Transactional
there are no exceptions of this kind as @Transactional
makes sure that thread has session when it's inside the transaction.
It solved problems for some time but I don't think that it's a good solution - making long-running methods transactional causes problems because other threads can't see changes made in database until the transaction is committed.
I created a java-pseudocode example to better illustrate my problem:
public class FirstThread {
...
@Transactional
public void processQueue() {
for(element : queue){
if(elementCanBeProcessed(element)){
elementDao.saveIntoDatabase(element);
secondThread.addToQueue(element.getId());
}
}
}
private boolean elementCanBeProcessed(element){
//code that gets a few objects from database and processes them
}
}
If I annotate the whole processQueue
method with @Transactional
the changes made in
elementDao.saveIntoDatabase(element);
won't be seen in secondThread
until the transaction is committed (so until the whole queue is processed). If I don't do that then the thread won't have session inside the elementCanBeProcessed
and it won't be able to access the database. I also can't annotate elementCanBeProcessed
instead because it's a private method in this class and I would have to move this into another class so that Spring proxy could work.
Is it possible to bind session to thread without making the whole method transactional? How should I manage sessions and transactions in background threads like that one?
Solution
I do not know of any Spring-ready solution for this. So, I think you need to implement one similar to the OpenEntityManagerInViewInterceptor class.
Basically, you need to use TransactionSynchronizationManager to bindResource() an instance of EntityManagerHolder for your thread when it is started and unbindResource() when your thread is finished.
The core part of OpenEntityManagerInViewInterceptor is:
if (TransactionSynchronizationManager.hasResource(getEntityManagerFactory())) {
...
}
else {
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewInterceptor");
try {
EntityManager em = createEntityManager();
EntityManagerHolder emHolder = new EntityManagerHolder(em);
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), emHolder);
...
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
}
}
Please post the code here as an answer if you implemented it.
Answered By - Amir Moghimi
Answer Checked By - Katrina (JavaFixing Volunteer)