1

I'm having this famous problem with LazyInitializationException in hibernate. I've seen a lot of question about this but still couldn't solve my problem.

I have a many to many relation like this:

Teen.java

public class Teen implements Serializable {

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name = "TEEN_FOLLOWER",
            joinColumns = @JoinColumn(name = "teenEmail"),
            inverseJoinColumns = @JoinColumn(name = "followerEmail"))
    private List<Follower> followerList;
}

Follower.java

public class Follower implements Serializable {

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name = "TEEN_FOLLOWER",
            joinColumns = @JoinColumn(name = "followerEmail"),
            inverseJoinColumns = @JoinColumn(name = "teenEmail"))
    private List<Teen> teenList;
}

One teen has n followers and one follower can follow n teens.

I have some entries in my database already and I'm fetching all the teens from it.

List<Teen> teens = (List<Teen>) teenDao.findAll();

for (Teen item : teens) {
    System.out.println("teen " + item.getEmail());
    List<Follower> followers = item.getFollowerList();
    for (Follower follower : followers) {
        System.out.println("follower " + follower.getEmail());
    }
}

I'm getting the exception on the above code when I try to read the followers list got from getFollowerList() method.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.capstone.server.model.Teen.followerList, could not initialize proxy - no Session
    org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:572)
    org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:212)
    org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:551)
    org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:140)
    org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:294)
    com.capstone.server.controller.TeenController.visualizar(TeenController.java:38)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:606)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:214)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:748)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:822)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

Really don't know what to do anymore. I already tried to add @Transaction annotation in my the method which causes the error and this worked. However when I send the Teen object to my android application, I get the same exception while converting the object to json.

My configuration files are:

servlet-context.xml

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

    <!-- DispatcherServlet Context: defines this servlet's request-processing 
        infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
        up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources 
        in the /WEB-INF/views directory -->
    <beans:bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <beans:bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <beans:property name="basename" value="classpath:messages" />
        <beans:property name="defaultEncoding" value="UTF-8" />
    </beans:bean>

    <beans:bean id="localeResolver"
        class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <beans:property name="defaultLocale" value="en" />
        <beans:property name="cookieName" value="myAppLocaleCookie"></beans:property>
        <beans:property name="cookieMaxAge" value="3600"></beans:property>
    </beans:bean>

    <interceptors>
        <beans:bean
            class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <beans:property name="paramName" value="locale" />
        </beans:bean>
    </interceptors>

    <!-- Configure to plugin JSON as request and response in method handler -->
    <beans:bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="jsonMessageConverter" />
            </beans:list>
        </beans:property>
    </beans:bean>

    <!-- Configure bean to convert JSON to POJO and vice versa -->
    <beans:bean id="jsonMessageConverter"
        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    </beans:bean>

    <!-- Enable @Transactional annotation -->
    <tx:annotation-driven />

    <mvc:interceptors>
        <beans:bean class="com.capstone.server.interceptor.LoginInterceptor" />
    </mvc:interceptors>

    <beans:bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

        <!-- Setting maximum upload size -->
        <beans:property name="maxUploadSize" value="1000000" />
    </beans:bean>

    <context:component-scan base-package="com.capstone.server" />

</beans:beans>

PersistenceJPAConfig.java

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class PersistenceJPAConfig {

    @Resource
    private Environment env;

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[] {
                Constants.PACKAGE_NAME
        });

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(additionalProperties());

        return em;
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        // Connection data
        dataSource.setDriverClassName(env.getRequiredProperty("db.driver"));
        dataSource.setUrl(env.getRequiredProperty("db.url"));
        dataSource.setUsername(env.getRequiredProperty("db.username"));
        dataSource.setPassword(env.getRequiredProperty("db.password"));
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    Properties additionalProperties() {
        Properties properties = new Properties();

        // Hibernate properties
        properties.setProperty("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
        properties.setProperty("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
        properties.setProperty("hibernate.format_sql",
                env.getRequiredProperty("hibernate.format_sql"));

        // Updates the database and generate tables, if needed
        properties.setProperty("hibernate.hbm2ddl.auto",
                env.getRequiredProperty("hibernate.hbm2ddl.auto"));

        // Initializes database with admin entry in User table
        properties.setProperty("hibernate.hbm2ddl.import_files",
                env.getRequiredProperty("hibernate.hbm2ddl.import_files"));
        properties.setProperty("hibernate.hbm2ddl.import_files_sql_extractor",
                env.getRequiredProperty("hibernate.hbm2ddl.import_files_sql_extractor"));

        return properties;
    }
}
14
  • 4
    possible duplicate of hibernate: LazyInitializationException: could not initialize proxy Commented Sep 25, 2015 at 19:05
  • Are you using Jackson to send object in Json notation to client ? Commented Sep 25, 2015 at 19:24
  • Yes, i'm using jackson.. check in servlet-context.xml -> MappingJackson2HttpMessageConverter Commented Sep 25, 2015 at 19:27
  • I think @JsonIgnore should work, when you fetch a Teen and Followers, Jackson sees that Followers have an association to Teen and tries to fetch it again even if it exists. But it doesn't find a session. Commented Sep 25, 2015 at 19:38
  • I added @JsonIgnore to followerList on Teen and the exception does not happen anymore.. however the followerList is not considered to send data to app.. it sends everything except from the follower list.. I wanted to send this info as well Commented Sep 25, 2015 at 19:44

5 Answers 5

2

Here's the way I ended up doing:

Instead I have only one teenDao.findAll() method that fetches only lazy data, I created another one receiving a boolean param forceLoad. The implementation goes like this:

@Transactional
public Teen find(String email) {
    return find(email, false);
}

@Transactional
public Teen find(String email, boolean forceLoad) {
    Teen teen = em.find(Teen.class, email);
    if(teen != null && forceLoad) {
        Hibernate.initialize(teen.getUser());
        Hibernate.initialize(teen.getFollowerList());
        Hibernate.initialize(teen.getPendingFollowerList());
        Hibernate.initialize(teen.getCheckInList());
    }
    return teen;
}

With this I initialize only the lists I desire when passing forceLoad as true.

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

Comments

1

With Transaction, when you have a LAZY FIELD, you cannot get the value of this field, because you are outside the Transaction, and your session is already released.

A way, put the transactionnal Annotations on your controler, or delegate the controler stuff, into a Service Class tagged as Transactionnal, and make your operation in the method of this service class.

Sure you cannot call a lazy Field outside the Transaction.

Regards,

Comments

0

You have to widen you transaction context so that the proxies can grab the rest of the entity's relation data:

@Transactional 
public void foo() {
   List<Teen> teens = (List<Teen>) teenDao.findAll();

    for (Teen item : teens) {
     ...
    }
}

4 Comments

Yes, I did this.. This worked at first.. but then when I send the List<Teen> to the internet (returning from foo() method) I get the same LazyInitializationException again..
That's what the service layer is for. You cannot hand over Entities outside the transactional boundary. Copy the data you need to pojos to pass then to higher layers.
Isn't there any alternative to @Transaction?
Yes. Either you stop returning entities from your dao, or call teen.getFollower().size() inside the dao to greedy fetch the entities too.
0

An other way is to let your Field with lazy loading, and pass by a query like this : * from Teen t JOIN FECTH t.teenList followerList

Regards,

FT

Comments

-1
public class Teen implements Serializable {

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "TEEN_FOLLOWER",
            joinColumns = @JoinColumn(name = "teenEmail"),
            inverseJoinColumns = @JoinColumn(name = "followerEmail"))
    private List<Follower> followerList;
}

you can fetch type eager instead of lazy.

It will fetch all the sub object while loading parent object.

But It will cause performance issue .

other solution You can try Session per request pattern

http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch02.html

3 Comments

Thanks for your answer, but unfortunately using EAGER is not an option for me.. it can cause serious problem of performance since I don't want to always fetch all followers of a teen.
@FelipeMosso Ya I accept that one ... Otherwise you can use Session per request to solve this
@FelipeMosso Please check the link skysoftarchive.wordpress.com/2009/07/14/…

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.