Java Multithreading with Hibernate Session and Spring
Working with multithreading on Spring and Hibernate can be quite frustrating, as the code ability to actually query and influence the database through Hibernates relies on the hidden Session and concept of “Unit of Work” abstracted by Spring’s strong infrastructures.
If we try to spawn a Thread from a @Scheduled process, or try to create a database transaction while handling an http request — we probably will encounter the common errors:
- LazyInitializationException
- Could not obtain transaction-synchronized Session for current thread
While these have their respected place among theorists — if we simply want to solve these and make things “work”, we just have a adhere to the basic concept of “working with the infrastructure” rather then “against the infrastructure”.
General Setup
To set up working with @Async please see reference:
https://www.baeldung.com/spring-async
Threads vs @Async
Creating Java threads using the Thread base class — is not natural to the Spring lifecycle and Hibernate session. Meaning, the session starts at the http handler or at the @Scheduled endpoint, etc — and is maintained by Spring on the running thread. If you wish to be able to both work multithreaded AND use hibernate’s session — you have to spawn threads using @Async calls — directly from the running thread.
Notice the 2 most important rules here: “@Async has two limitations
- It must be applied to public methods only.
- Self-invocation — calling the async method from within the same class — won’t work.”
From the description and the illustration, remember — the async function must be placed in a different component from the caller.
Blocking and waiting for threads
When we use the Thread class, and extend it, we usually use the function join() to attach to the process and wait for the results. The equivalent of this when using @Async would be the use of the function result as a Future object. Specifically, if the Async function should have returned a String — you should have it return a Future<String> instead. And for the result, instead of returning a String result — you return
return new AsyncResult<String>(“your-result-here”);
In the example below, we have used Boolean — as a replacement of a void function. We just needed to return something, so the Async thread can be in our control