4

I have entities like this:

@Entity
@Table(name = "ASSESSMENT")
public class Assessment {

    //All other fields..

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "assessment")
    @OrderBy(value = "order ASC")
    private List<AssessmentPart> assessmentParts = new LinkedList<>();

    public List<AssessmentPart> getAssessmentParts() {
        return assessmentParts;
    }

    //All other getters/setters
}

The other one:

@Entity
@Table(name = "ASSESSMENT_PART")
public class AssessmentPart {

    //All other fields

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "ASSESSMENT_ID", nullable = false)
    private Assessment assessment;

    public Assessment getAssessment() {
        return assessment;
    }

    public void setAssessment(Assessment assessment) {
        this.assessment = assessment;
    }

    //All other getters/setters
}

Assessment parts are lazy loaded from assessment entity. I also have spring controller method which is Transactional and loads assessment part from the database:

@Transactional
public void doSomething(String partId, Map<String, Object> model) {

    AssessmentPart assessmentPart = //laods a part with entity manager
    Assessment assessment = assessmentPart.getAssessment(); //Getting the assessments

    model.put("assessmentParts", assessment.getAssessmentParts()); //adding all assessments parts into spring model map
}

Once I've added assessment parts into spring model map, they become available in my JSP page and I am using JSTL to loop through them:

<c:forEach var="assessmentPart" items="${assessmentParts}">
     //Not loading any lazy stuff, just getting an ID of assessment part
</c:forEach>

The exception is thrown from this JSP page:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: my.package.something.Assessment.assessmentParts, could not initialize proxy - no Session

How is this possible if this collection has already been loaded in a transaction? I am just trying to loop through, hibernate shouldn't be loading anything at this point, because it was already loaded. Why is this weird thing happening?

4
  • It is initializing the collection not the content of that collection (you get a bunch of proxies for each row). Also it (IMHO) is a bad idea to make your web method transactional, your service should be transactional. Either use a OpenSessionInViewFilter or use the Hibernate.initialize method to properly initialize the collection. Commented Sep 27, 2013 at 10:26
  • OpenSessionInViewFilter won't work in my case, because I am using Hibernate via JPA, the thing that helped was: OpenEntityManagerInViewFilter, now it works, but is it good approach? Commented Sep 27, 2013 at 10:30
  • You didn't mention you where using JPA so I assumed plain hibernate. If it is a good solution depends on your application. There is no general rule to apply (only opinions I guess :) ). If you don't want this then you need to properly initialize your collection (either by the Hibernate.initialize method or create a custom query to load the data needed (forcing eager fetching of the dependend resources). Commented Sep 27, 2013 at 10:35
  • Actually OpenEntityManagerInViewFilter is the second worst solution (better than using fetch = FetchType.EAGER because you can restrict it to a certain url, but bad because you keep the session open during a user request). Using Hibernate.initialize is better since you can do that in the specific @Transactional service method. Having the rest of the @Many/OnetoMany collections Lazy and using a custom join fetch query is the best solution => only one query instead of two or more for the Hibernate.initialize. Commented Jan 18, 2015 at 10:36

3 Answers 3

8

In the view the entitymanager is already closed and as such the elements in your collections fail to retrieve there properties. The code you wrote in the controller doesn't initialize the elements in the collection (it is a LAZY collection) but only the collection is initialized (not the elements inside it).

Either force the entitymanager to remain open by configuring the OpenEntityManagerInViewFilter in your web configuration.

Or change your controller code to include a call to Hibernate.initialize to properly initialize your collection.

@Transactional
public void doSomething(String partId, Map<String, Object> model) {

    AssessmentPart assessmentPart = //laods a part with entity manager
    Assessment assessment = assessmentPart.getAssessment(); //Getting the assessments
    Hibernate.initialize(assesment.getAssesmentParts()); // Init collection
    model.put("assessmentParts", assessment.getAssessmentParts()); //adding all assessments parts into spring model map
}

Either that or create a custom query which forces eager fetching of the collection.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. Now I have some options. Will do some investigations which is the best practice.
Having collections be lazy and using fetch join (either by Hibernate or JPA) is the best solution since it uses only one query.
2

Change

model.put("assessmentParts", assessment.getAssessmentParts());

to

Hibernate.initialize(assessment.getAssessmentParts());

to initialize your collection.

The method call assessment.getAssessmentParts() is not initializing your collection. This method call will just return you a collection wrapper and this wrapper you need to pass to the Hibernate.initialize method for manual initialization.

EDIT:

With JPA, you can fetch the AssessmentParts along with the Assessment (wherever needed) using JPQL Fetch Joins, overriding the default behavior:

SELECT a FROM Assessment asmt LEFT JOIN FETCH asmt.assessmentParts WHERE asmt.id = :id

2 Comments

I am not using Hibernate directly, I am using it via JPA.
Btw, Hibernate also has a fetch join (search for 'fetch join' here docs.jboss.org/hibernate/orm/3.3/reference/en/html/…). It is better then the Hibernate.initialize since it is using only one query.
0

Loading AssessmentPart from EntityManager will Load Assessment @ManyToOne(fetch = FetchType.EAGER)

AssessmentPart assessmentPart = //laods a part with entity manager
Assessment assessment = assessmentPart.getAssessment();

Trying to extract AssessmentParts from assessment wont work since it's not @OneToMany(fetch = FetchType.LAZY) and session is already closed

model.put("assessmentParts", assessment.getAssessmentParts());

Get list of AssessmentParts by Assessment from EntityManager in new method should solve the problem.

1 Comment

Loading assessment parts from assessment works just fine, I am in the same transaction.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.