• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Paul Clapham
  • Ron McLeod
Sheriffs:
  • Jeanne Boyarsky
  • Liutauras Vilda
Saloon Keepers:
  • Tim Holloway
  • Carey Brown
  • Roland Mueller
  • Piet Souris
Bartenders:

Hibernate take 2: lazy loading issue

 
Sheriff
Posts: 67756
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I used Hibernate for a small project a while back. While I liked the vastly decreased amount of code required over using raw JDBC, I had enough problems with it that I shelved it for the next few projects in the interest of getting them done quickly. I suspect that most of the problems I had were due to not really understanding the "what goes where and why" of Hibernate, and so now I'm giving it another go in a slightly more complex project (in which I have the luxury of taking the time to learn Hibernate principles properly).

I've got basic things running but am having an issue with lazy loading of a many-to-one association.

I have a class named Credentials that has a many-to-one association defined as follows:

The code in question performs a query for a Credentials instance (it's not being loaded by primary key, so I need the query) and then attempts to fetch the associated Member instance:

When executed, I get the following:

Line 145 is the log.info() just before the return statement.

If I uncomment the log.info() just before the commit, all is well. So I gather that Member is not being realized before it gets accessed in some way and that has to happen while the persistence context is active.

It's obviously ridiculous to use a logging statement to trigger a semantic action in the code, so what's the correct way to ensure that Member is realized before closing the session without performing some hokey operation upon it just to trigger its realization?

I strongly suspect there's some Universal Hibernate Truth that I'm missing again.

Thanks for any insight.
[ April 26, 2008: Message edited by: Bear Bibeault ]
 
Bear Bibeault
Sheriff
Posts: 67756
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
P.S. The above is a simplification of the actual code that illustrates my question. The actual code is more robust about making sure that the transaction and sessions are properly closed.

P.P.S. Is the transaction really necessary when doing a query?
 
author
Posts: 4356
45
jQuery Eclipse IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This may or may not be related, but have you tried using container managed transactions? It's not often I call a method to explicitly begin/end a transaction in a J2EE application.
 
author and cow tipper
Posts: 5009
1
Hibernate Spring Tomcat Server
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
So, the Credentials object comes back, and it has an association with a Member. The Member is associated in a Lazy manner, so the Member is only loaded when the Credentials object needs it.

So, in this query, a Credentials object comes back initialized, but Hibernate does you the favor of only bringing back proxies for the many possible Member instances. The proxies are placeholders that will only ever be initialized if you explicitly ask for them through the getter of the Credentials object.

Now, the problem with lazy loading is that those getters on the Credentials object will only work if they are called within the confines of the transaction that loaded the the original Credential. If the transaction is committed without a member getter ever being called on the Credential, the transactional context is closed, and your Credential object has no way of using Hibernate to get the associated Member. The Hibernate Session wipes its hands clean of the associated credentials object once the transaction is comitted - the Credential becomes a detached instance once the transaction is committed, as opposed to a persistent instance that is managed by the Hibernate Session.

This method call though:

//log.info(" member: "+ member);

Does force Hibernate to replace the proxy with a real object. The transaction is live, so the data comes back. The POJO on the JVM is now real, and that's why this little gem of code makes everything work - the PROXY is replaced by a real object, DURING a transaction, and the member POJO lives on in the JVM after the transaction is committed.

I have a little tutorial on How Hibernate Works that discusses this.

How Hibernate Works

Of course, this doesn't solve the problem...

So, what you can do is mark the Credential to have a FetchType of EAGER, as opposed to LAZY. This way, all the Member objects will be loaded when the credential is loaded.

Another option is to extend the life of the transaction. Many 'simple' web applications use a single-transaction-per-cycle pattern (anti-pattern?) so the whole request-response cycle is done within a single transaction, which ends any LazyLoading problems. I think you can figure some drawbacks to that of course.

Another option might be to do a full load of the Credential object before doing the logging. Of course, that would probably cause more potential problems to log.

>>P.P.S. Is the transaction really necessary when doing a query?

I've been on a couple of blog sites (Googled them but couldn't find them), but I've read one of the writers of Hibernate (Bauer or King, can't remember) get very 'testy' when asked about this. Something about asking this question to their students after a week long class, and then throwing erasers at everyone that gets it wrong. Yes, a transaction is needed. Certainly, a Hibernate Session is needed to load data from the database, and an open, working, queryable session goes hand in hand with an open transaction.

And even more to the point, with one to many relationships, it may have be the same session that originally loaded the one side of the relationship. With a new session, the one side of the relationship will need to be reattached to the Hibernate Session, lest it be detached and trigger another LazyLoadingException.

By the way, JQuery in Action was very well done.

-Cameron McKenzie

[ April 27, 2008: Message edited by: Cameron Wallace McKenzie ]
[ April 27, 2008: Message edited by: Cameron Wallace McKenzie ]
 
Bear Bibeault
Sheriff
Posts: 67756
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Cameron, that's pretty much what I figured was going on, and...

Originally posted by Cameron Wallace McKenzie:
[QB]So, what you can do is mark the Credential to have a FetchType of EAGER, as opposed to LAZY. This way, all the Member objects will be loaded when the credential is loaded.


that was what I was looking for!

By the way, JQuery in Action was very well done.

And thank you for that.
 
Bear Bibeault
Sheriff
Posts: 67756
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Scott Selikoff:
This may or may not be related, but have you tried using container managed transactions?

Baby steps... just getting started. Besides, that sounds like something available only in a JEE container?
 
Bear Bibeault
Sheriff
Posts: 67756
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

but I've read one of the writers of Hibernate (Bauer or King, can't remember) get very 'testy' when asked about this.

Interesting. The reason is that in thumbing through Bauer's book, he seems to skip the transactions on queries and I didn't know if that was just a brevity thing or not.
 
Ranch Hand
Posts: 177
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Great question and great thread!

I was using Hibernate as a simple tool to replace my hand written SQL statements, and then ran up against this dreaded LazyInitializationError. Now I at least know why.

Has Hibernate grown 'a feature too far'? Does it have to do all of this stuff in the background. I liked it when I thought it was a simpler tool. This new paradigm will take me a while to get used to.
 
Ranch Hand
Posts: 138
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Sorry for the Thread-necromancy, but, amazingly, this is the only thread on the forum that appears when I search for "LazyInitializationError".

Yes, I'm getting the LazyInitializationError, while trying to write a Spring/Hibernate/Quartz application. The app runs as a SpringContextLoaderServlet. There is no "view" to speak of, since there's no UI. The context is loaded at startup, the jobs are scheduled, and away we go!

I'm using a Dao package in a separate Jar, where all Dao classes extend Spring's org.springframework.orm.hibernate3.support.HibernateDaoSupport.

Here is my context.xml (just the database bits, since the Quartz bits are working fine...):


If I understand correctly, the hibernateInterceptor should intercept any method calls on any of those Dao instances, and if necessary open a session. After which, the HibernateDaoSupport will use whatever local Session is already open to execute the various Dao methods (correct me if I'm wrong).

Here's the java code that's causing the problem:



Here's what I get in the (copious) logs:


I have no @Transactional annotations or anything, so I'm not sure why HibernateTemplate would always flush the session so eagerly (or even whether that matters or not)?

Please GOD, someone help! I've struggled with this LazyInitializationError for months now, with no one able to give me a solution. I just want to be able to use a Spring HibernateDaoSupport inside a non-MVC, non-UI web app with no view. Is that so hard?

If it helps at all, I have a separate admin applciation that uses the same Dao library, with an OSIV interceptor, without any problem at all. So I know it must be technically possible, somewhere in the guts of Spring / Hibernate.

Help!
 
Philippe Desrosiers
Ranch Hand
Posts: 138
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
UPDATE: So was reading this and it seems that the HibernateInterceptor is set up to Open a session (if necessary) every time one of the "target" class methods is called, then immediately close it once the method returns.

Leave aside for the moment that I can't begin to imagine a scenario where this would be the slightest bit useful (Think of a call to dao.get() method, followed by setting some properties, followed by a dao.save(). This would throw an unbound session exception of some sort). My point is, that I had misunderstood the purpose of the interceptor.

So I guess what I need, is for this interceptor to come into play when calling the executeInternal() method of my quartz Job instead!

...Of course, there's no way to configure this in Spring, since the Job is created ad hoc at runtime by the scheduler, rather than by Spring at Context load-time. So I can't reference it in my interceptor configuration.

If anyone knows a way to do this, let me know. Until then, I guess I'll keep searching...
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
reply
    Bookmark Topic Watch Topic
  • New Topic