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;
    }
}
Bilal BBB
  • 1,154
  • 13
  • 20
Felipe Mosso
  • 3,907
  • 11
  • 38
  • 61
  • 4
    possible duplicate of [hibernate: LazyInitializationException: could not initialize proxy](http://stackoverflow.com/questions/345705/hibernate-lazyinitializationexception-could-not-initialize-proxy) – Hannes Sep 25 '15 at 19:05
  • Are you using Jackson to send object in Json notation to client ? – Bilal BBB Sep 25 '15 at 19:24
  • Yes, i'm using jackson.. check in servlet-context.xml -> MappingJackson2HttpMessageConverter – Felipe Mosso Sep 25 '15 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. – Bilal BBB Sep 25 '15 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 – Felipe Mosso Sep 25 '15 at 19:44
  • Does it work if you add @JsonIgnore to teenList instead of followerList – Bilal BBB Sep 25 '15 at 19:46
  • Does it work when you add @JsonIgnore to teenList ? – Bilal BBB Sep 25 '15 at 19:53
  • Yes, it worked, but then I cannot send all the teens followed by a follower, right? – Felipe Mosso Sep 25 '15 at 21:21
  • I haven't yet found a solution for that. – Bilal BBB Sep 25 '15 at 22:02
  • Now, you know what is the problem, if you find an alternative solution, please share it here. – Bilal BBB Sep 25 '15 at 22:08
  • Yes, I'm working with that.. Actually I'm considering to use some kind of json ignore as you mentioned, but I'm looking for a way to do it dynamically, since it is not always I can ignore the list of teens a follower is following – Felipe Mosso Sep 25 '15 at 22:09
  • this may help http://wiki.fasterxml.com/JacksonFeatureObjectIdentity – Bilal BBB Sep 25 '15 at 22:11
  • the problem is that I don't handle with jsons.. I use MappingJackson2HttpMessageConverter.. all I do is to return the object fulfilled with the values and this converter does all the trick to me – Felipe Mosso Sep 25 '15 at 22:20
  • matt's answer http://stackoverflow.com/questions/3340485/how-to-solve-circular-reference-in-json-serializer-caused-by-hibernate-bidirecti – Bilal BBB Sep 25 '15 at 22:22

5 Answers5

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.

Felipe Mosso
  • 3,907
  • 11
  • 38
  • 61
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,

fabien t
  • 358
  • 1
  • 9
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) {
     ...
    }
}
Hannes
  • 2,018
  • 25
  • 32
  • Yes, I did this.. This worked at first.. but then when I send the List to the internet (returning from foo() method) I get the same LazyInitializationException again.. – Felipe Mosso Sep 25 '15 at 19:19
  • 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. – Hannes Sep 25 '15 at 19:25
  • Isn't there any alternative to @Transaction? – Felipe Mosso Sep 25 '15 at 19:35
  • Yes. Either you stop returning entities from your dao, or call `teen.getFollower().size()` inside the dao to greedy fetch the entities too. – Hannes Sep 29 '15 at 17:02
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

fabien t
  • 358
  • 1
  • 9
-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

Siva Kumar
  • 1,983
  • 3
  • 14
  • 26
  • 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. – Felipe Mosso Sep 25 '15 at 19:08
  • @FelipeMosso Ya I accept that one ... Otherwise you can use Session per request to solve this – Siva Kumar Sep 25 '15 at 19:10
  • @FelipeMosso Please check the link https://skysoftarchive.wordpress.com/2009/07/14/implementing-hibernates-session-per-request-pattern-using-spring-mvc/ – Siva Kumar Sep 25 '15 at 19:13