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?
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
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
Related
I stumbled into this problem somewhat surprisingly.
But the requirement is actually nothing special. Using Cache and Async from inside the same service.
I created a repository to demonstrate the problem: https://github.com/MBurchard/cachedemo
There are two versions:
v1: Without "self injection" only calling the service methods from controller
v2: with self injection to test all.. calling from internal and external.
As long as you don't need another proxy, because you don't want to use #Async in the same class, this also works with version v2, i.e. with the "self inject" via InitializingBean.
But even this requirement to have #Async and #Cacheable in the same service is not too much of a stretch now.
Of course I don't want to use both annotations on the same method, that's for sure.
But as you can see in version v2, only the #Async calls, but not the #Cacheable calls, work externally, for example from a controller.
But internally, using the self reference, only the #Cacheable calls work, but not the #Async calls.
It looks like Spring injects into the controller one proxy and into the service (itself) the other proxy.
As you can see in version v1, without the self injection both, #Cacheable and #Async, work from the controller...
Solutions:
Do not use #Cacheable but use Caffeine or Guava Cache directly...
Use Runables: Spring #Async method inside a Service ... But I don't think it's a pretty solution
Split the Service... Not always possible and can also lead to less maintainable code.
Suggestions?
I am upgrading my application from Spring3 to Spring 4. In my existing application we were using OpenSessionInViewInterceptor which creates the transaction along with new request.
Now I am using JPA also so even if I use OpenEntityManagerInViewInterceptor it doesn't associate transactions with it. So I had to put transaction manually in all my controller methods.
Due to legacy code, the controller classes have lots of business logic instead of distributing it properly to service layer.
Now if I put #Transctional annotation on controller method, then it doesn't work whereas if I use manual transaction through DefaultTransactionDefinition then it works properly.
Also, if I remove the business logic of controller methods to service layer, and put #Transactional annotation to service layer, then it also work fine. But these changes are tedious and my application is very big, so cant do it.
Please let me know why #Transactional doesn't work in my case if I put it in controller, how should I get it working.
I am not using AspectJ for transaction management.
Tried approaches:
Code at both places : code in dispatcher-servlet.xml along with appliacationContext.xml but still the controller transactions are failing.
Moved bean for urlMapping into applicationContext.xml but still it fails.
I'm using the #Transactional annotation for my entire Dao class with spring and hibernate. Everything works great, it's just that I would like to omit the behaviour for a few non database related methods in my Dao.
http://static.springsource.org/spring/docs/2.5.4/reference/transaction.html#transaction-declarative-annotations
Every time I step into these methods during a debugging session, I always get to JdkDynamicAopProxy.class which is super annoying.
Is there any way to omit transactional for specific methods? Or at least fix this annoying debugging behavior?
In Spring you can put the #Transactional annotation on only the methods you want to make transactional, instead of putting it at the class level.
Otherwise perhaps consider extracting those specific non-transactional methods into a separate class?
I'm working on a Spring MVC project and trying to integrate Apache Shiro for the security. Everything was going just swimmingly until I realized Hibernate was prematurely closing the session/connection after a single query and causing a lazyinit exception. Not surprising, what I was doing should be done within a transaction so the session isn't closed.
Dilemmas…
I tried putting #Transactional on my controller method, but I get 404's then. Looking at my logs, I can see that when Spring is bootstrapping it ignores any mappings in my HomeController if that #Transactional annotation is on any method within the controller.
Without the #Transactional and it loads up just fine, and Ih can see the RequestMappingHandlerMapping bean sees all the #RequestMapping annotations in my controller.
With #Transactional but without DefaultAdvisorAutoProxyCreator, and it works except Shiro annotations are simply ignored.
tldr: Shiro requires DefaultAdvisorAutoProxyCreator, but if I create that bean, Spring blows up when using the #Transactional annotation.
I'm asking for help because I'm completely at a loss for how to proceed at this point.
This is typically because when you put #Transactional on a method, Spring creates a dynamic proxy for that bean - if the bean implements an interface then dynamic proxy is created based on that interface, otherwise CGLIB will be used for creating the proxy.
The problem in your case, I am guessing is because you probably have based your controller on some interface, or you are extending it based on a base class. This is ending up creating a proxy based on that interface, because of this when it comes time for mappings to be created for your request, Spring MVC is probably not finding your mappings from your bean.
The fix could be a few:
a. You can try and force proxies to be based on CGLIB for your transactions:
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
b. You can use pure Aspectj,either load time weaving or compile time weaving
c. You can move the #Transactional into a Service (which has an interface) and delegate the call from the controller to the service, this way avoiding #Transaction on the controller
These days i am learning spring by http://static.springsource.org.
I am facing some problem in this page http://static.springsource.org/docs/Spring-MVC-step-by-step/part4.html. i am not getting it clearly that when setProductManager method is called when InventoryController class is invoked. I know that this works as a front controller and when hello.jsp page is requested ,ModelAndView method is executed of InventoryController. but i want to know that when setProductManager method is called.
Any help would be appreciable.
Spring is an ioc container and in this particular example the dependency-injection is implemented using setters (setter injection). Basically the container takes care of supplying your bean (controller in this case) with necessary dependencies.
Back to your question: dependency injection is performed before your bean is ever used by the framework or any other beans requiring it. Furthermore, controllers are singletons. This means setProductManager is called before any request is handled by the controller - when the application is started. And because there is only one instance of the controller - it is called once.