Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating JPA session for background threads

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?

like image 360
Paweł Chorążyk Avatar asked May 19 '14 08:05

Paweł Chorążyk


1 Answers

Here is the code I wrote after reading Amir Moghimi's answer. It seems a little bit 'hacky' because the documentation says that neither EntityManagerHolder nor TransactionSynchronizationManager should be used directly by a typical application code.

@Service
public class DatabaseSessionManager {

    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;

    public void bindSession() {
        if (!TransactionSynchronizationManager.hasResource(entityManagerFactory)) {
            EntityManager entityManager = entityManagerFactory.createEntityManager();
            TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(entityManager));
        }
    }

    public void unbindSession() {
        EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
                .unbindResource(entityManagerFactory);
        EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
    }
}

It seems to be working - the session is bound to my thread between bindSession() and unbindSession() calls and I don't have to create a transaction to achieve it.

like image 70
Paweł Chorążyk Avatar answered Oct 12 '22 21:10

Paweł Chorążyk