I was perplexed when the creation of a projection from a lazily loaded entity collection didn't throw a LazyInitializationException, when returning a DTO response from a stateless REST service's update method. So I checked, and the Hibernate Session was still open, even after the transaction has completed and was no longer active (as per the printout):
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@PutMapping(value = "/{employeeId}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public EmployeeDto updateEmployee( @PathVariable Long employeeId, @Valid @RequestBody UpdateEmployeeDto updateEmployeeDto) {
Employee employee = employeeService.updateEmployee(employeeId, updateEmployeeDto);
System.out.println("Transaction Active: " +
TransactionSynchronizationManager.isActualTransactionActive()); // prints false
Session hibernateSession = entityManager.unwrap(Session.class);
System.out.println("Hibernate Session Open: " + hibernateSession.isOpen()); //prints true
return employeeMapper.toDto(employee);
}
I understood that by default, session management in Spring was tightly coupled with the transactional context, i.e. the session is scoped to the current database transaction. So why didn't it close after triggering the previous tx commit?
A bare @Transactional is used on the update employee method, and there is no extended persistence context applied.
I remember getting LazyInitExceptions when converting to DTO projections from previous projects, I must be missing something apparent, though there is no special configuration done to the transaction/session management of this project.
Sessions are not bound to Transactions. They are bound to the http call (or websocket call).
You can verify this by calling a lazy loaded subentity entity in a Scheduler. This will result in a LazyInitializationException
because Schedulers do not have a Session associated.
Transactions and Sessions are different. Transactions complete when the the method annotated with @Transactional is completed. Commits (Database, MessageBroker, usw...) are executed when a Transaction completes.
Sessions hold more information like the security context. This exists for the entires "endpoint call" and will therefore stay open until the Request is completed.