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:
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.
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
Specific Difference Between Bufferedreader and Filereader
Is It Bad Practice to Use Reflection in Unit Testing
JSON Consumer of Polymorphic Objects
How to Load an Org.W3C.Dom.Document from Xml in a String
Differencebetween a Hashmap and a Treemap
Joda-Time: What's the Difference Between Period, Interval and Duration
Are Java Static Calls More or Less Expensive Than Non-Static Calls
Difference Between Openjdk and Adoptium/Adoptopenjdk
Differencebetween Putting a Property on Application.Yml or Bootstrap.Yml in Spring Boot
How to Do Query Auto-Completion/Suggestions in Lucene
Using Eclipse Java Compiler (Ecj) in Maven Builds
How to Set Jvm Parameters for Junit Unit Tests