Creating a JPA Session for Background Threads

We use Hibernate through JPA and Spring to control the persistence of objects in our web application. We use the open-session-in-view template to create sessions for streams that respond to HTTP requests. We also use some threads that do not generate views - they just wake up from time to time to do their job. This creates problems because they do not have a session open by default, so they throw exceptions, such as

org.hibernate.SessionException: Session is closed! 

or

 could not initialize proxy - no Session

We found that if each background thread invokes its logic in a method annotated with @Transactional, there are no such exceptions, since it @Transactionalensures that the thread has a session when it is inside a transaction.

He solved the problems for some time, but I don’t think this is a good solution - creating long-running transaction methods causes problems because other threads cannot see the changes made in the database until the transaction is completed.

I created an example java pseudocode 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 method processQueueusing @Transactionalchanges made to

elementDao.saveIntoDatabase(element);

secondThread , (, ). , elementCanBeProcessed, . elementCanBeProcessed , , , - Spring .

, ? , ?

+4
4

Spring . , , , OpenEntityManagerInViewInterceptor.

, TransactionSynchronizationManager bindResource() EntityManagerHolder , , unbindResource(), .

OpenEntityManagerInViewInterceptor:

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

, , .

+2

, Amir Moghimi answer. "", , EntityManagerHolder, TransactionSynchronizationManager .

@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());
    }
}

, - bindSession() unbindSession(), .

+6

, , :

  • @Transactional processQueue
  • elementCanBeProcessed ElementProcessor @Component elementCanBeProcessed @Transactional
  • elementProcessor.elementCanBeProcessed(element);

elementProcessor bean, Spring, TransactionInterceptor.

+1

, /. , JPA JTA @Transactional:

Spring :

@PersistenceContext
private EntityManager entityManager;    

, :

EntityManager em = entityManager.getEntityManagerFactory().createEntityManager();

Spring DAO, , JPA ..

private void save(EntityManager em) {

    try
    {         
        em.getTransaction().begin();                

        <your database changes>

        em.getTransaction().commit();                        
    }
    catch(Throwable th) {
        em.getTransaction().rollback();
        throw th;
    }        
}

JPA beans @Configuration, , , .

0

Source: https://habr.com/ru/post/1541077/


All Articles