Hi, let me start by pinpointing this problem again.
In my CategoryController (Spring MVC) I am trying to lazy load topics, other categories, i.e. anything that has been declared as fetch = FetchType.LAZY in my AbstractCategory domain object:
Code:
@MappedSuperclass
public abstract class AbstractCategory implements java.io.Serializable {
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "category")
public Set<Category> getCategories() {
return this.categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "category")
public Set<Topic> getTopics() {
return this.topics;
}
Note: I used the myEclipse reverse generate tool to create my DAO's and domain objects.
Now, moving on to my controller. First of all, the reason why I tried to use annotations was because of its elegance. I really don't want to use programmatic style transaction demarcations (and I am sure there is a solution to this using annotations)
In the controller below, just like the issue I was getting when I tried to lazily get Topics, in this case I am trying to get more categories of the parent category BUT I get a LazyLoadingException.
Code:
public class CategoryController extends SimpleFormController {
public ModelAndView onSubmit(Object command) throws ServletException {
CategoryBean categoryBean = (CategoryBean) command;
System.out.println("the name from the command bean is: "+categoryBean.getName());
// call category service
//iCategoryService
ICategoryService categoryService = (ICategoryService)DataAccessServiceFactory.getBean("iCategoryService");
ITopicService topicService = (ITopicService)DataAccessServiceFactory.getBean("iTopicService");
List<Category> categories = categoryService.findByName("Autos", null);
// should only be one category in actuality
for (Category category : categories) {
System.out.println("category: "+category.getName());
Set<Category> subCategories = category.getCategories(); LAZY EXCEPTION HERE
for (Category subCategory : subCategories) {
System.out.println("subCategory: "+subCategory.getName());
//Set<Topic> topics = subCategory.getTopics();
List<Topic> topics = topicService.findByCategoryID(subCategory.getCategoryId(), null);
System.out.println("****now to get topics if there are any!****");
for (Topic topic : topics) {
System.out.println("topic: "+topic.getTitle());
System.out.println("****now to get submissions if there are any!****");
Set<Submission> submissions = topic.getSubmissions();
for (Submission submission : submissions) {
System.out.println("submission topic: " +submission.getTopic());
System.out.println("submission title: " +submission.getTitle());
}
}
}
}
Note, I tried to hack this thing by avoiding:
Code:
//Set<Topic> topics = subCategory.getTopics();
because I was getting a lazy exception, I thought I could just create my topic service (because it is declared @Transactional) and then pass the sub-categoryID
Code:
List<Topic> topics = topicService.findByCategoryID(subCategory.getCategoryId(), null);
but same deal, the second I tried to get sub-categories for my category, it choked, got the lazy load exception again...
kesa, you are absolutely correct about findbyname and the session being closed. In my categoryService, I do the following:
Code:
public class ICategoryServiceImpl implements ICategoryService{
@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
public List<Category> findByName(Object name, int[] rowStartIdxAndCount){
List<Category> result = iCategoryDAO.findByName(name,rowStartIdxAndCount);
return result;
}
Essentially, my method findByName is declared:
Code:
@Transactional(propagation=Propagation.REQUIRED, readOnly=true)
This is why this works:
Code:
List<Category> categories = categoryService.findByName("Autos", null);
but when the minute I start looping through the categories to get sub-categories of each category:
Code:
Set<Category> subCategories = category.getCategories();
I get:
Code:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.logixplayer.pf.domain.Category.categories, no session or session was closed
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:583)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:112)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
root cause
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.logixplayer.pf.domain.Category.categories, no session or session was closed
org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:343)
org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:163)
com.logixplayer.pf.category.controller.CategoryController.onSubmit(CategoryController.java:43)
org.springframework.web.servlet.mvc.SimpleFormController.onSubmit(SimpleFormController.java:409)
org.springframework.web.servlet.mvc.SimpleFormController.onSubmit(SimpleFormController.java:381)
org.springframework.web.servlet.mvc.SimpleFormController.processFormSubmission(SimpleFormController.java:267)
org.springframework.web.servlet.mvc.AbstractFormController.handleRequestInternal(AbstractFormController.java:265)
org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:807)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:112)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
Yes the session is being closed which is why I am trying to use OpenEntityManagerInViewFilter to begin with!
I am not clear on your first suggestion about t.callMethod();
The actual error is happening when I try to get a lazy collection. (not after when I iterate over topics or categories)
How can I bind to the original entityManager session? i.e. the one that was used for (using annotations):
Code:
List<Category> categories = categoryService.findByName("Autos",null);
because it is closed after this call: categoryService.findByName
Essentially what I want to do is Set<Category> subCategories = category.getCategories(); and just move on.
Great exercise but can we just resolve this now??
Thanks