How do I properly do a background thread when using Spring Data and Hibernate? How do I properly do a background thread when using Spring Data and Hibernate? multithreading multithreading

How do I properly do a background thread when using Spring Data and Hibernate?


With Spring you don't need your own executor. A simple annotation @Async will do the work for you. Just annotate your heavyMethod in your service with it and return void or a Future object and you will get a background thread. I would avoid using the async annotation on the controller level, as this will create an asynchronous thread in the request pool executor and you might run out of 'request acceptors'.

The problem with your lazy exception comes as you suspected from the new thread which does not have a session. To avoid this issue your async method should handle the complete work. Don't provide previously loaded entities as parameters. The service can use an EntityManager and can also be transactional.

I for myself dont merge @Async and @Transactional so i can run the service in either way. I just create async wrapper around the service and use this one instead if needed. (This simplifies testing for example)

@Servicepublic class AsyncService {    @Autowired    private Service service;    @Async    public void doAsync(int entityId) {        service.doHeavy(entityId);    }}@Servicepublic class Service {    @PersistenceContext    private EntityManager em;    @Transactional    public void doHeavy(int entityId) {        // some long running work    }}


What happens is, probably, you have transaction on your DAO piece of code and Spring is closing the session on transaction close.

You should squeeze all your business logic into single transaction.

You can inject SessionFactory into your code and use SessionFactory.openSession() method.
The problem is, that you will have to manage your transactions.


Method #1: JPA Entity Manager

In background thread: Inject entity manager or get it from Spring context or pass it as reference:

@PersistenceContextprivate EntityManager entityManager;    

Then create a new entity manager, to avoid using a shared one:

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

Now you can start transaction and use Spring DAO, Repository, JPA, etc

private void save(EntityManager em) {    try    {                 em.getTransaction().begin();                        <your database changes>        em.getTransaction().commit();                            }    catch(Throwable th) {        em.getTransaction().rollback();        throw th;    }        }

Method #2: JdbcTemplate

In case you need low-level changes or your task is simple enough, you can do it with JDBC and queries manually:

@Autowiredprivate JdbcTemplate jdbcTemplate;

and then somewhere in your method:

jdbcTemplate.update("update task set `status`=? where id = ?", task.getStatus(), task.getId());

Side note: I would recommend to stay away from @Transactional unless you use JTA or rely on JpaTransactionManager.