session.beginTransaction(); Post post = getPost(); // calling post.getComments() will get real comment objects from Hibernate here... session.commit(); // You cant call getComments() here because theres no active session. // Youll die with a LazyInitializationException. post.getComments();
You might think that Hibernate would open a new connection or session if it has to lazy load associations. This is a bad idea, because you can get a ton of open connections.
You might think that Hibernate would keep a session open on a thread, so you can do many operations in a single transaction. Well, it can. If you can get hold of a sessionFactory, you can call sessionFactory.getCurrentSession() and it will return you the ThreadLocal session back.
Except that Im using a JTA transaction model in a J2EE web application. Oops.
EDIT: ARGH. I misread the error message. JTA works fine, but Hibernate needs a current JTA transaction and I didn't have that.
You might think that Hibernate would have a servlet filter that would open up a single transaction at the beginning of a page render and close it when the page finished rendering. And, well, Spring does. Hibernate is a persistence solution, so from what I can tell, theyve essentially washed their hands of Spring. But Spring has OpenSessionInViewFilter.
However, I cant get OpenSessionInView to work. Heres my code:
public String execute() throws Exception {
// this is a Post object extracted by another action and set on this one
Post post = getPost();
String parsedPost = parsePost(post);
setParsedPost(parsedPost);
}
// Get various lazy loaded objects if they exist (we only want to pull them in if we are
// displaying posts)
public String parsePost(Post pPost) {
...
Map media = pPost.getMedia();
...
List comments = pPost.getComments();
}
and heres what happens when I try it:
2005-12-29 23:38:27,515 DEBUG [com.tersesystems.blog.dao.hibernate.HibernatePostDAO] - <findPublishedPage: pPage = 0, pPageSize = 5>
2005-12-29 23:38:27,531 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - <Opening Hibernate Session>
2005-12-29 23:38:27,625 DEBUG [org.springframework.orm.hibernate3.HibernateTemplate] - <Eagerly flushing Hibernate session>
2005-12-29 23:38:27,640 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - <Closing Hibernate Session>
2005-12-29 23:38:27,640 DEBUG [com.tersesystems.blog.dao.hibernate.HibernatePostDAO] - <findPublishedPage: posts = [[...]>
2005-12-29 23:38:27,765 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - <Opening Hibernate Session>
2005-12-29 23:38:27,765 DEBUG [org.springframework.orm.hibernate3.HibernateTemplate] - <Eagerly flushing Hibernate session>
2005-12-29 23:38:27,765 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - <Closing Hibernate Session>
2005-12-29 23:38:27,765 ERROR [org.hibernate.LazyInitializationException] - <could not initialize proxy - the owning Session was closed>
org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:53)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:84)
at org.hibernate.proxy.CGLIBLazyInitializer.intercept(CGLIBLazyInitializer.java:134)
at com.tersesystems.blog.dao.hibernate.PostImpl$$EnhancerByCGLIB$$9066ad74.toString(<generated>)
at java.lang.String.valueOf(String.java:2577)
at java.lang.StringBuilder.append(StringBuilder.java:116)
at com.tersesystems.blog.action.ParsePostAction.parse(ParsePostAction.java:108)
at com.tersesystems.blog.action.ParsePostAction.execute(ParsePostAction.java:137)
at com.opensymphony.xwork.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:283) What this seems to say is that OpenSessionInView will only keep a session open in an object that extends from HibernateDaoSupport, and uses a HibernateTemplate. I tried overriding getHibernateTemplate() so I could add better logging to it, but someones declared it final and private. Which is unfriendly.
Theoretically the way to do this is to use the HibernateCommentDAO and have it take in a postId, but that seems silly to me. I dont want to have to define a transaction explicitly around every action. I just want to be able to call post.getComments() anywhere in the page that Im rendering, which I should be able to do.
I'd write my own filter, but I don't want to mess with the existing Session support that exists. I started writing a filter behind the OpenSessionInView one and found out that I had two different instances of sessionFactory. I don't know how the heck I managed that one.
I think what I'm going to do is dismantle all the HibernateTemplate stuff and just have a straight transaction + session filter wrapping the request. Then I'll access it from the DAO and everything will be happy.