Need Proxy?

BotProxy: Rotating Proxies Made for professionals. Really fast connection. Built-in IP rotation. Fresh IPs every day.

Find out more


Spring MVC + Hibernate: could not initialize proxy - no Session

Question

Note: See my own answer to this question for an example of how I solved this issue.

I am getting the following exception in my Spring MVC 4 + Hibernate 4 project:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mysite.Company.acknowledgements, could not initialize proxy - no Session

After reading a lot of other questions about this issue, I understand why this exception occurs, but I am not sure how to fix it in a good way. I am doing the following:

  1. My Spring MVC controller calls a method in a service
  2. The service method invokes a method in a DAO class
  3. The method in the DAO class fetches an entity object through Hibernate and returns it to the calling service
  4. The service returns the fetched object to the controller
  5. The controller passes the object to the view (JSP)
  6. The view tries to iterate on a many-to-many association that is lazily loaded (and is thus a proxy object)
  7. The exception is thrown because the session is closed at this point, and the proxy cannot load the associated data

I have previously worked with PHP and doctrine2, and this way of doing things caused no problems. I am trying to figure out the best way to solve this, because the solutions that I found so far don't seem so great:

  • Load the association eagerly, potentially loading lots of unnecessary data
  • Call Hibernate.initialize(myObject.getAssociation()); - this means that I have to loop through associations to initialize them (I guess), and just the fact that I have to do this, kind of makes the lazy loading less neat
  • Using a Spring filter to leave the session open in the views, but I doubt that this is a good thing to do?

I tried to use @Transactional in my service, but without luck. This makes sense because I am trying to access data that has not yet been loaded after my service method returns. Ideally I would like to be able to access any association from within my view. I guess the drawback of initializing the associations within my service is that I have to explicitly define which data I need - but this depends on the context (controller) in which the service is used. I am not sure if I can do this within my controller instead without losing the abstraction that the DBAL layer provides. I hope that makes sense. Either way, it would be great if I didn't have to always explicitly define which data I want to be available to my view, but just let the view do its thing. If that is not possible, then I am just looking for the most elegant solution to the problem.

Below is my code.

View

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<h1><c:out value="${company.name}" /> (ID: <c:out value="${company.id}" />)</h1>

<c:forEach var="acknowledgement" items="${company.acknowledgements}">
    <p><c:out value="${acknowledgement.name}" /></p>
</c:forEach>

Controller

@Controller
public class ProfileController {
    @Autowired
    private CompanyService companyService;

    @RequestMapping("/profile/view/{id}")
    public String view(Model model, @PathVariable int id) {
        Company company = this.companyService.get(id);
        model.addAttribute("company", company);

        return "viewCompanyProfile";
    }
}

Service

@Service
public class CompanyServiceImpl implements CompanyService {
    @Autowired
    private CompanyDao companyDao;

    @Override
    public Company get(int id) {
        return this.companyDao.get(id);
    }
}

DAO

@Repository
@Transactional
public class CompanyDaoImpl implements CompanyDao {
    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public Company get(int id) {
        return (Company) this.sessionFactory.getCurrentSession().get(Company.class, id);
    }
}

Company entity

@Entity
@Table(name = "company")
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    // Other fields here

    @ManyToMany
    @JoinTable(name = "company_acknowledgement", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "acknowledgement_id"))
    private Set<Acknowledgement> acknowledgements;

    public Set<Acknowledgement> getAcknowledgements() {
        return acknowledgements;
    }

    public void setAcknowledgements(Set<Acknowledgement> acknowledgements) {
        this.acknowledgements = acknowledgements;
    }

    // Other getters and setters here
}

Acknowledgement entity

@Entity
public class Acknowledgement {
    @Id
    private int id;

    // Other fields + getters and setters here

}

mvc-dispatcher-servlet.xml (a part of it)

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan">
            <list>
                <value>com.mysite.company.entity</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <tx:annotation-driven />

Thanks in advance!

Answer

The simplest and most transparent solution is the OSIV pattern. As I'm sure you aware, there is plenty of discussion about this (anti)pattern and the alternatives both on this site and elsewhere so no need to go over that again. For example:

https://stackoverflow.com/questions/1103363/why-is-hibernate-open-session-in-view-considered-a-bad-practice

However, in my opinion, not all criticisms of OSIV are entirely accurate (e.g. the transaction will not commit till your view is rendered? Really? If you are using the Spring implementation then https://stackoverflow.com/a/10664815/1356423)

Also, be aware that JPA 2.1 introduced the concept of Fetch Graphs which give you more control over what is loaded. I haven't tried it yet but maybe this if finally a solution for this long standing problem!

http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html

cc by-sa 3.0