0

I'm using Spring with JPA and Hibernate. I've got some DAO-Classes that are annotated with @Repositoy and some Controller-Classes. When I call one of the dao's methods in my controller to load some entities I get back the Entity and after that, I want to get some other entities that are stored in a field of the first loaded entity. But at that time spring has already closed the session and lazy loading is no longer possible.

My db.xml configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" default-autowire="byName">

    <!-- Scans within the base package of the application for @Components to configure as beans -->
    <bean id="placeholderConfig"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:db.properties" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!-- <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="jpaVendorAdapter" />
    </bean> -->
    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="generateDdl" value="true" />
                <property name="databasePlatform" value="${db.dialect}" />
            </bean>
        </property>     
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.url}" />
        <property name="username" value="${db.username}" />
        <property name="password" value="${db.password}" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />
</beans>

The method in the Dao is annotated as follows:

@Transactional(readOnly = true, propagation=Propagation.REQUIRED)

now I wanna do something like this:

@Controller
public class HomeController{

    @Autowired
    private UserDao userDao;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ResponseEntity<String> home(){
        ...
        User user = userDao.findUser(id);
        Set<Order> orders = user.getOrders();
        ...
    String myResult = ...;
    return jsonService.generateResponse(myResult);
    }

}

@Repository
public class UserDao{

    @PersistenceContext
    private EntityManager entityManager;

    public User findUser(Integer id){
        return entityManager.find(User.class, id);
    }
}

The set of orders should be lazy-loaded but i get the following exception: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:...,no session or session was closed

root cause: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ..., no session or session was closed

I tried to annotate the method in Controller wirt @Transactional and also to set mode="aspectj" in annotation-driven property in the db.xml, but nothing did work. Is there any way to lazy load the orders of the user?

For any help, thanking you in anticipation!

mheck
  • 179
  • 4
  • 14

1 Answers1

5

You can use special filter to get session in web view. Add to your web.xml

<filter>
    <filter-name>jpaFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>jpaFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

the filter works as follows:

  • The filter intercepts a servlet request
  • The filter opens an EntityManager and binds it to the current thread
  • Web controller is called
  • Web controller calls service
  • Transaction interceptor begins a new transaction, retrieves the thread-bound EntityManager and binds it to the transaction
  • Service is called, does some stuff with EntityManager, then returns
  • Transaction interceptor flushes the EntityManager then commits the transaction
  • Web controller prepares view, then returns
  • View is built
  • Filter closes the EntityManager and unbinds it from current thread
zacheusz
  • 8,750
  • 3
  • 36
  • 60
  • I'm returning just JSON. So the problem should not be the web view that tries to lazy load entities. the error occures in the controlers method. – mheck Jun 25 '13 at 17:37
  • I'm using this for JSON with spring rest and works like a charm. It has a little performance overload (look at my description) but should work. Just try it :) – zacheusz Jun 25 '13 at 17:40
  • it really works. where do i have to put my @Transactional annotations? DAO or Controller? I think the performance should be much better then always fetch the entities eager, don't you think so? – mheck Jun 25 '13 at 17:45
  • if you fetch everything eager then not, but you can use other fetching method like join fetch. Check this question: http://stackoverflow.com/questions/14360216/mvc-with-lazy-loading – zacheusz Jun 25 '13 at 17:49