I'm writing this down so I can reflect on it when the healing ends. I basically spent the last five days moping around the house figuring that my life as a below average programming hack was over. (Fortunately this is just a hobby and I have a day job.) I was trying to use Hibernate with MySQL along with Spring 3.2's Hibernate Transaction Management so I didn't have to worry as much about cleaning up after myself. I'm using Spring's MVC for controlling the user interface stuff.
The issue I was running into initially was a "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" when returning my view and whipping through the objects. This kind of made sense to me since I knew the Hibernate Transaction Management piece (annotated as @Transactional) was doing its job of closing the Session.
My first whack at things were to Eagerly load by objects. While this did fix the issue initially, I quickly found that this was the wrong, the very wrong, approach. I was loading my object, my object's collection of objects, those object's objects, those objects collection of objects, and so on. Once could argue my object relationships could use some scrubbing, but that aside, Eager loading was not the answer.
If you Google LazyInitializationException you'll get a number of references to using Spring's OpenSessionInViewFilter. The logic here is that by using this filer, or writing your own if you're smart enough, is that it has hooks into the Transaction Manager such that together they will keep the Session open until the request has finished. This means that you can still Lazy load things, and whip through stuff in your view. Then after request is finished, the OpenSessionInViewFilter will take care of closing the Session. This was perfect!!!
But like most things in my life, getting his implemented wasn't as easy as you'd think. After wiring up the filter, I didn't get any change in behavior. Still got the error message "org.hibernate.LazyInitializationException: could not initialize proxy - no Session." I gave the OpenSessionInViewInterceptor version a try, but no go. Same error.
Stepping back a second. When I first got started writing this application a few weeks back, I noticed some odd behavior. When I launched the application and my beans were created, I was ending up with two database connections showing up on the MySQL side. Without any requests coming in, I should just have one. But I never thought much of it though. And as it turned out, I should have really paid attention to this.
I don't have much experience in doing this, but I was at such a point in my disillusion, that I attached the Spring 3.2 source to my libraries and started walking through the OpenSessionInViewFilter and HibernateTransactionManager code. In here I could see where the filter "binds" a newly created Session to the hibernate transaction manger and then where the transaction manager tries to fetch that Session from its binding. For some reason, the transaction manager was never finding the Session that the filter was putting in there. You could see the filter open a Session, then the transaction manger open a second Session after failing on a Session lookup. Even a dummy like me found this fishy.
I'm not sure if was the morning coffee or evening beer, but something fired in my mind and made me think back to those two database connections. Could there be a relationship? And as it turns out, there was.
I'm sure it's much more complicated that this, or much more simple, but essentially what was happening was I was instantiating Spring twice. I was loading it once with my MVC Dispatcher Servlet and then again with the Context Listener. Along with everything else, this was creating two HibernateTransactionManager beans. So I believe what was happening was, the OpenSessionInViewInterceptor was setting a Session on one transaction manager bean, but the other transaction manager bean was taking care of the transaction pieces and thus not finding the Session that the filter gave it.
After all this, I stopped instantiating Spring twice and the world started to align for me. So far the OpenSessionInViewFilter is working just fine. I'm able to lazy load my options and properly return my views. For a ton of reading in this problem a good place to start is issuing this Google search "site:stackoverflow.com opensessioninviewfilter lazyinitializationexception".
Monday, April 01, 2013
Notes on Spring's OpenSessionInViewFilter and Hibernate's LazyInitializationException
Posted by Mac Noland at 12:49 PM
Subscribe to:
Post Comments (Atom)
2 comments:
Interesting observation. I got stuck at: 'instantiating Spring twice'. What do you mean by it? what was the configuration you were using to do so? And what did you change to make it a single instance?
I am trying to work-around this to dynamically change the datasource in controller and adopt for transactional services.
Thanks,
Prabhakar
Coming in late here, but...
Yes, there are a couple of scenarios where Spring keeps global state that will kill you if you have more than one Spring context loaded. The multiple contexts will stomp all over each other's globals. This *is* documented, but there's also documentation showing you how to load multiple contexts that does *not* warn you about these cases, so it's a pretty easy trap to fall into.
The two cases that I know of are the ThreadLocal variables used to bind the Hibernate Session to the transaction manager, and anything injected into AspectJ aspects. I've blogged about this in more detail here: http://lagod.id.au/blog/?p=266
Post a Comment