This week's book giveaway is in the OCPJP forum. We're giving away four copies of OCA/OCP Java SE 7 Programmer I & II Study Guide and have Kathy Sierra & Bert Bates on-line! See this thread for details.
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 ]
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.
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.
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.
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).
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.
Joined: Mar 29, 2006
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...