Reputation: 353
The context of this question is within spring-boot, using spring-data-jpa and hibernate.
A colleague wrote an @Service
and annotated the service method with @Transactional
. The service method loads an entity, and subsequently hits a one-to-many lazily loaded collection (fetch = FetchType.LAZY
). The service method is invoked by some custom delegator, which i will come back to. This works fine when invoked from a @RestController
endpoint.
When i invoked the service from a camel route (again via the custom delegator) it barfed with a lazy initialization exception.
On digging, found that the service implements an interface, the custom delegator looks up the service (it is injected so has proper proxy) and calls a method
on the interface which is actually a java-8 default method. This default-method then locally calls the @Transactional
method.
So there's the problem :- this is a LOCAL method call so the aspecting/proxy-ing of the @Transactional
annotation is not done (we use aspectJAutoProxy) so the method is NOT invoked within a transaction, so the lazy-loading SHOULD fail. And to double-check, also tried it via an @Scheduled
annotation: same behaviour. Barfs like it should.
My Question: So why does it work when called from the @RestController
? This is driving me nuts!
There is no transactional annotation on the rest controller endpoint.
I added some debug code to the service using TransactionSynchronizationManager.isActualTransactionActive()
and it shows that in no case is there a transaction, even when being called via the controller endpoint.
So why does the lazy loading work when being called from the controller? I dumped all SQL and at no points are the lazy-collection already loaded, so they are not in any hibernate cache.
I remember reading once that lazy loading was a hint, not a command, but still... why does it work in that one case?
Upvotes: 3
Views: 1323
Reputation: 353
after being perplexed by this on many occasions, have stumbled across the answer:
sprint-boot is doing an open-entity-manager-in-view behind our backs via the OpenEntityManagerInView interceptor. Had no idea this was going on.
See this excellent answer from Vlad Mihalcea https://stackoverflow.com/a/48222934/208687
Upvotes: 1
Reputation: 2279
When your method annotate to transactional hibernate session close after return method , if object that return from method have lazy , lazy property not load and you get exception that session close. You can use fetch in query or using OSIV
Upvotes: 1