How to 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?

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)

@Service
public class AsyncService {

@Autowired
private Service service;

@Async
public void doAsync(int entityId) {
service.doHeavy(entityId);
}
}

@Service
public class Service {

@PersistenceContext
private EntityManager em;

@Transactional
public void doHeavy(int entityId) {
// some long running work
}
}

Spring Cassandra Repository - Saving a record in a background thread

When a CompletableFuture is completed exceptionally, there's no stacktrace because the exception is still unhandled. It's stored until the user does something that "activates" that exception. For example calling get() would directly throw that exception.

When doing more complex things with CompletableFuture the exception is stored along the results of the future (hence BiConsumer to have result and exception as parameters), but it's up to you to check if there is an exception and handle it.

Since you can chain the futures and therefore encounter multiple exceptions, you end up with documentation like the following:

If the supplied action itself encounters an exception, then the
returned stage exceptionally completes with this exception unless this
stage also completed exceptionally.

If you understand that on the first read, you're talented.

Obtaining a new session from Hibernate for a background thread

You can create an threading service, which extends HibernateAccessor, as a standalone Spring service, defined at spring.xml, and send to it code/data you want to process. Something like this:

    Session session = SessionFactoryUtils.getSession(
getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
SessionHolder sessionHolder = null;
try {
applyFlushMode(session, false);
sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
Transaction t = getSessionFactory().getCurrentSession().beginTransaction();
try {

//execute your code here

t.commit();
} catch (Exception e) {
t.rollback();
log.error("Error", e);
}
try {
flushIfNecessary(sessionHolder.getSession(), false);
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
} finally {
SessionFactoryUtils.closeSession(sessionHolder.getSession());
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}

Spring AbstractRoutingDataSource background threads

I fixed it by using a session scoped spring bean which stores my tenant.

See link for sample

how to create a background task in spring to keep listening to data on TCP socket

Write the socket handler in ContextListener and then it'll be a part of the Main thread and won't die unless the application itself.

Hibernate Session Threading

Make your Runnable a Spring bean and add @Transactional annotation over run. You must be warned thou that this asynchronous task won't run in the same transaction as your web request.

And please don't start new thread, use pooling/executor.

Process REST request to retrieve data via Hibernate in a separate Thread

The HibernateTemplate should allow you to create a new Hibernate Session because the current SpringSessionContext ThreadLocal storage has no Session bound.

Related to the design, you should close the ScrollableResults and release the database related resources (connection, cursor).

I would therefore design it like this:

  1. The initial request builds a Command that is assigned an UUID and the Command is passed to an ExecutorService to be processed asynchronously. The result of the execution is cached.

  2. Any subsequent request uses the same UUID to fetch the computation Result from the Cache. You can use a Future so that the client code blocks untile the Computation is over.

The asynchronous block calculating the Result object must always close the Hibernate session and free the database connection resources.



Related Topics



Leave a reply



Submit