Some of my learnings while working with Hibernate in a multi-threaded Spring Boot application.
Multiple sessions
When you start an @Async
method, it runs in its own thread managed by Spring. This means that the session created by the calling method may not be shared with the async method. The async method might create its own session.
This behaviour also happens when you use @Transactional(Transactional.TxType.REQUIRES_NEW)
when there is a transaction already. This creates a new session as Hibernate has to suspend the current transaction in the current session.
Session is a database session that’s usually one to one with a database connection.
In this multi-session scenario, there are few things you need to be aware of.
1. Un-proxied objects (new transaction)
class ServiceA { @Transaction public void fetch() { Entity entity = // fetched from DB serviceB.process(entity); } } class ServiceB { @Transactional(Transactional.TxType.REQUIRES_NEW) public void process(Entity entity) { LazyEntity lazy = entity.getLazyAssociation(); // <- this can throw an exception } }
When you use REQUIRES_NEW, the previous transaction is suspended and a new transaction is initiated via a new session/connection. So if you try to query something on the entities fetched from the previous session, you may get this exception:
Hibernate: illegally attempted to associate a proxy with two open Sessions
The fix is to unproxy the proxied object using this utility method from Hibernate:
LazyEntity lazy = Hibernate.unproxy(entity.lazyEntity, LazyEntity.class);
2. java.lang.IllegalStateException: Session/EntityManager is closed or this.session is null
class ServiceA { public void fetch() { Entity entity = // fetched from DB serviceB.process(entity); } } class ServiceB { @Async public void process(Entity entity) { LazyEntity lazy = entity.getLazyAssociation(); // <- this can throw an exception } }
If you have fetched an entity in a session and pass that entity to be processed by an async method which is connected to a different session, Hibernate will throw an exception like this:
java.lang.IllegalStateException: Session/EntityManager is closed
or
this.session is null
This is happening because the session created by the original thread (the thread that called the async method) could have finished its processing and closed the session it owned.
The fix is to refetch the entity from this new method:
LazyEntity lazy = repository.findById(entity.getId()).getLazyAssociation();
MOST COMMENTED
Flutter
Flutter Setup
React Native
Learn React Native with a Board Game (Part 1 of 4)
jQuery / Web Development
jQuery DataTable: Sorting dynamic data
Uncategorized
Hibernate – Associations are not loaded
Database / Java / MySQL / Spring Boot
Hibernate Error – Encountered problem trying to hydrate identifier for entity
Spring Boot / Uncategorized
Working with Hibernate in a multi-threaded application
Web Development
Designing REST APIs