4

How to use lazy loading in Spring MVC? I'm using eager at this moment, but this makes my app works slowler. This is part of my domain:

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "NEWS_TAG", joinColumns = @JoinColumn(name = "NEWS_ID"), inverseJoinColumns = @JoinColumn(name = "TAG_ID"))
private List<Tags> tags = new ArrayList<Tags>();

public List<Tags> getTags() {
    return this.tags;
}

And dao:

public List<News> getSomeNews(long b, long hm) {

    List<News> news = (List<News>) sessionFactory
            .getCurrentSession()
            .createQuery(
                    "from News WHERE title!='About' ORDER BY publish_time")
            .setMaxResults((int) hm).setFirstResult((int) b).list();
    return news;
}

Servlet-context:

    <context:annotation-config />

    <context:component-scan base-package="net.babobka.blog" />



    <import resource="../../db/db-config.xml" />

    <bean id="urlForwardController"
        class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />

    <bean id="tilesConfigurer"
        class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/tiles.xml</value>
            </list>
        </property>
    </bean>

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass"
            value="org.springframework.web.servlet.view.tiles2.TilesView" />
    </bean>

</beans>

Db-config:

<bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="/WEB-INF/db/jdbc.properties" />

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.databaseurl}" p:username="${jdbc.username}" p:password="${jdbc.password}" />

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation">
            <value>/WEB-INF/db/hibernate.cfg.xml</value>
        </property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${jdbc.dialect}</prop>
                <prop key="hibernate.show_sql">${jdbc.show_sql}</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:annotation-driven />

</beans>

Web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <!-- The definition of the Root Spring Container shared by all Servlets 
        and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>

        <param-value>
        /WEB-INF/spring/root-context.xml
        /WEB-INF/spring/application-security.xml
        </param-value>
    </context-param>



    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>



    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>*.gif</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>hibernateFilterChain</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hibernateFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>




</web-app>

What I have to do to solve my problem?

babobka
  • 79
  • 1
  • 8

2 Answers2

6

You can use OpenSessionInViewFilter to prevent hibernate session get closed. Add this to web.xml:

  <filter>
    <filter-name>hibernateFilterChain</filter-name>
    <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>hibernateFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

@see read more here: OpenSessionInViewFilter

And remove fetch = FetchType.EAGER. @ManyToMany is LAZY by default.

seralex.vi
  • 620
  • 3
  • 6
  • No bean named 'sessionFactory' is defined – babobka Aug 01 '13 at 08:43
  • @user2639377: If you using JPA entity managed you need replace `filter-class` with `org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter`. Please provide your spring context configuration. – seralex.vi Aug 02 '13 at 18:26
  • @user2639377: where configuration related to database connection? This file content "../../db/db-config.xml" required – seralex.vi Aug 03 '13 at 10:13
  • @user2639377: From you code: `List news = (List) sessionFactory.getCurrentSession()`. Where and how defined `sessionFactory`? – seralex.vi Aug 03 '13 at 10:25
  • `@Autowired private SessionFactory sessionFactory;` in DAOImpl – babobka Aug 03 '13 at 10:28
  • If it `@Autowired` and must be defined in xml spring configuration. I need this definition! What version of Hibernate you using? – seralex.vi Aug 03 '13 at 10:34
  • Look at the question. I added this. 3.6.9.Final - version. – babobka Aug 03 '13 at 10:38
  • Ok! Replace `org.springframework.orm.hibernate4.support.OpenSessionInViewFilter` with `org.springframework.orm.hibernate3.support.OpenSessionInViewFilter` because you using Hibernate 3 and it must working – seralex.vi Aug 03 '13 at 10:41
  • SEVERE: Exception starting filter hibernateFilterChain java.lang.ClassNotFoundException: org.springframework.orm.hibernate3.support.OpenSessionInViewFilte??r – babobka Aug 03 '13 at 10:46
  • Add dependency to spring-orm-${spring.version}.jar – seralex.vi Aug 03 '13 at 11:01
  • ` org.springframework spring-orm ${org.springframework-version} jar ` I have this. Is it not correct? – babobka Aug 03 '13 at 11:04
  • What is strange ?? in your `org.springframework.orm.hibernate3.support.OpenSessionInViewFilte*??*r` – seralex.vi Aug 03 '13 at 11:09
  • Don't know, but I changed it to org.springframework.orm.hibernate.support3.OpenSessionInViewFilter and console shows me without "?". – babobka Aug 03 '13 at 11:11
  • Oh. I change hibernate.support3 to hibernate3.support and now I'm having No bean named 'sessionFactory' is defined again – babobka Aug 03 '13 at 13:24
  • According to this http://stackoverflow.com/questions/8574552/spring-hibernate-lazy-loading-sessionfactory-and-opensessioninviewfilter I should add sessionFacroty to root context. But how? – babobka Aug 03 '13 at 13:31
  • Add web.xml file to your question description – seralex.vi Aug 03 '13 at 15:05
  • Ok. I did it.This website's validator makes me angry. – babobka Aug 03 '13 at 15:10
  • Remove `init-param` block from `DispatcherServlet` and move all content from file `/WEB-INF/spring/appServlet/servlet-context.xml` to file `/WEB-INF/spring/root-context.xml` including `db-config.xml` content – seralex.vi Aug 03 '13 at 15:30
  • org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/appServlet-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/appServlet-servlet.xml] Also I didn't remove content from servlet-context.xml and db-context.xml. – babobka Aug 03 '13 at 15:40
  • Just create `/WEB-INF/appServlet-servlet.xml` without any bean definitions – seralex.vi Aug 03 '13 at 15:42
  • WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/blog/] in DispatcherServlet with name 'appServlet' – babobka Aug 03 '13 at 15:57
  • I just added and It works. Thank you, guy! – babobka Aug 03 '13 at 16:43
1

Change your annotation to (fetch = FetchType.LAZY). Be aware that if you're passing the result to some code outside the transaction (such as a view template), you might encounter errors if associated objects needed by the external code haven't already been loaded.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • No. With adding FetchType.LAZY only I will have some errors with session or something. – babobka Jul 31 '13 at 19:14
  • Your comment isn't clear. Are you saying that if you change to LAZY you start getting errors? Are those errors the result of trying to access unloaded associated entities after you've left the transaction? – chrylis -cautiouslyoptimistic- Jul 31 '13 at 19:16
  • I've got this: failed to lazily initialize a collection of role: net.babobka.blog.domain.News.tags, no session or session was closed – babobka Jul 31 '13 at 19:20
  • Also I use @Transaction in services only. – babobka Jul 31 '13 at 19:20
  • "Session was closed" means that your code tried to read information out of the `tags` field, but the field hadn't been loaded, and it couldn't load it because the transaction (session) was over. You can't lazy-load more data after the end of the transaction; you have to eager-load, specifically load the fields that will be used, or execute the code that gave you the error inside the transaction. – chrylis -cautiouslyoptimistic- Jul 31 '13 at 19:23
  • >specifically load the fields that will be used But how? – babobka Jul 31 '13 at 19:25
  • Access the list somehow, such as by calling `tags.size()`. This is a known difficulty with lazy-loading, and if you know you're going to be reading those tags, you're not saving anything by using it. In fact, depending on how intelligent your ORM is, you may be adding unnecessary database round trips. – chrylis -cautiouslyoptimistic- Jul 31 '13 at 19:28
  • Can you show me some examples? Sorry for my English. It's not my mother tongue. – babobka Jul 31 '13 at 19:31
  • Not specifically. Lazy loading should only be used when you usually won't be accessing the field and it's expensive to load, and it means that if you later do want the field, the ORM system has to go back to the database to find it. You either don't use lazy loading, you force the ORM to load the specific fields you know you'll want, or you run everything inside the transaction. If you expect that you will usually use `tags`, using lazy loading just makes the initial load faster by making reading `tags` take longer later. – chrylis -cautiouslyoptimistic- Jul 31 '13 at 19:36
  • Ok. I don't want to use LAZY anymore. How to do it with EAGER? I mean getting tag's fileds without binded fields like News and etc. And please show me some code. It says more than words. – babobka Jul 31 '13 at 19:43
  • You showed a snippet but not what class the first one is; it looks like it belongs to `News`. Just use `FetchType.EAGER` and everything that's needed will be loaded. – chrylis -cautiouslyoptimistic- Jul 31 '13 at 19:46
  • No. I don't want. I have a little query which have to return me 14 tag rows. But console shows me about 50 queries with news. But I don't want news fields. – babobka Jul 31 '13 at 19:49