Richard
Richard

Reputation: 353

Lazy loading works, but shouldn't

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

Answers (2)

Richard
Richard

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

ali akbar azizkhani
ali akbar azizkhani

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

Related Questions