Search code examples
springhibernatelazy-loadingtransactionallazy-initialization

Getting LazyInitializationException despite being in @Transactional and having no detached entities


I’m using Spring 3.2.11.RELEASE, JPA 2.1, Hibernate 4.3.6.Final, and MySQL 5.5.37. I’m getting the below error in a JUnit test

testSendValidAssignment(org.mainco.subco.thirdParty.service.ThirdPartyServiceIT)  Time elapsed: 13.366 sec  <<< ERROR!
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.mainco.subco.lessonplan.domain.LessonPlan.classrooms, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:575)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:214)
    at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:155)
    at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:278)
    at org.mainco.subco.ThirdParty.service.ThirdPartyServiceIT.testSendValidAssignment(ThirdPartyServiceIT.java:102)

However, I don’t know what line is to blame for this error message. In my method I have

@Service
@Transactional
public class MyServiceImpl implements MyService
{
....

@Override
public void sendAssignment(final String assignmentId)
{
    final Assignment assignment = m_lessonPlanDao.getAssignment(assignmentId);
    if (processData(assignment))
    {
        // Gather the students who have been assigned this assignment
        final List<Classroom> classes = lessonPlan.getClassrooms();
        System.out.println("got " + classes.size() + " classes.");
        // Send one request for each class assignment
        for (final Classroom classroom : classes)
        {
            final List<User> classStudents = m_classroomSvc.findClassStudents(classroom.getId());
            System.out.println("got " + classStudents.size() + " students.");

and both System.out lines print numbers. There are no other references to this lazily-loaded collection in the method so I don’t know what else to check in my data. Any advice is appreciated.


Solution

  • Your service method is transactional, but your entire test method is not. The size checking of the element is performed in an assertion statement in your JUnit test and is done outside of the transaction scope of the service, so it causes lazy initialization exception. There are three ways you can go

    • you can try call size() method inside your dao method to make outside call of the size() method safe.
    • you can force the user of your method to open transaction(document which objects are detached and make it a contract/or use infamous open session in view pattern)
    • you can create a DTO layer and return size as a part of DTO

    all three solutions have pros and cons:

    the first solution will look weird for the supporters of the code when they'll find that you call a getter for no obvious reson - it needs commenting.

    the second makes life harder to the users of the interface

    the third violates DRY principle

    p.s. you can also disable lazy initialization of course