What is a transaction?
A transaction (@Transactional in Spring) is a unit of work with the database.
Inside it:
Outside it:
Why does this matter?
In Hibernate/JPA, relationships like @OneToMany are usually lazy by default.
Example:
@Entity
class User {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Offer> offers;
}
When Hibernate loads a User, it doesn’t load offers. Instead, it puts a proxy object (PersistentSet) in place.
That proxy can only be initialized if a Session is open.
Lazy association = loaded on demand.
User user = userRepository.findById(id).orElseThrow();
user.getOffers(); // not loaded yet
int size = user.getOffers().size(); // triggers SELECT * FROM offers ... if inside a tx
What happens outside a transaction?
If you call .size() (or loop, or access fields) on that proxy:
Hibernate wants to run SQL.
But the Session is closed.
Hibernate throws:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
We added logging inside OfferAdminMapperDecorator:
try {
loggingService.info(LogPrefix.OFFER, LogLayer.MAPPER,
"User {} offers size: {}", user.getId(),
(user.getOffers() == null ? "null" : user.getOffers().size()));
} catch (LazyInitializationException e) {
loggingService.error(LogPrefix.OFFER, LogLayer.MAPPER,
"Could not initialize user.getOffers() for user {}: {}", e, user.getId(), e.getMessage());
}
userQueryAdminService.getUserEntityById(...) returns a User.User.offers is still a lazy proxy..size() tries to run SQL but fails → LazyInitializationException.That’s why our logs look like:
Could not initialize user.getOffers() for user [...] failed to lazily initialize a collection
The same thing happened for:
org.getOffers().size()emp.getCreatedOffers().size()