springhibernatejakarta-eespring-mvcopen-session-in-view

Configuring OpenSessionInView filter with Spring Hibernate Application


I am working on a Spring Hibernate web application

Earlier I was loading the Spring Configuration with just dispatcher-servlet.xml without using the ContextLoaderListener but when I implement the OpenSessionInView pattern I have to provide a ContextLoaderListener in web.xml and create a new applicationContext.xml & move the hibernate configuration from dispatcher-servlet.xml to applicationContext.xml.

I have some doubts regarding this change.

Below is the code which is working fine. web.xml

<display-name>PetClinic</display-name>

    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/applicationContext.xml
        </param-value>
        </context-param>

     <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>


    <servlet>
      <servlet-name>dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
        </init-param>
      <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
      <servlet-name>dispatcher</servlet-name>
      <url-pattern>/forms/*</url-pattern>
    </servlet-mapping>

    <filter>
      <filter-name>hibernateFilter</filter-name>
      <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
      <init-param>
         <param-name>sessionFactoryBeanName</param-name>
         <param-value>sessionFactory</param-value>
      </init-param>
   </filter>

   <filter-mapping>
     <filter-name>hibernateFilter</filter-name>
     <url-pattern>/forms/*</url-pattern>
   </filter-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

dispatcher-servlet.xml

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

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
            p:basename="messages"/>

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">

        <property name="prefix">
            <value>/WEB-INF/view/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

applicationContext.xml

 <context:annotation-config />

    <!-- <context:property-placeholder> XML element automatically registers a new PropertyPlaceholderConfigurer 
    bean in the Spring Context. -->
    <context:property-placeholder location="classpath:database.properties" />

    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="hibernateTransactionManager"/> 

    <!-- Creating DataSource -->
      <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${database.driver}" />
        <property name="url" value="${database.url}" />
        <property name="username" value="${database.user}" />
        <property name="password" value="${database.password}" />
      </bean>

    <!-- To persist the object to database, the instance of SessionFactory interface is created. 
SessionFactory is a singleton instance which implements Factory design pattern. 
SessionFactory loads hibernate.cfg.xml and with the help of TransactionFactory and ConnectionProvider 
implements all the configuration settings on a database. -->

<!-- Configuring SessionFactory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>com.petclinic.Owner</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>             
            </props>
        </property>
    </bean>

<!-- Configuring Hibernate Transaction Manager -->
    <bean id="hibernateTransactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

A. Can anyone tell me the reason for creating a new applicationContext.xml and moving the hibernate code to it? Why not just let the code be in dispatcher-servlet.xml?

B. To use filters in Spring do we need a ContextLoaderListener, without it won't the filter work?


Solution

  • This is because the spring application has usually two contexts: a root context and a per servlet dispatcher context.

    The idea behind this is that an application can have multiple servlet contexts with beans such as the controllers, and each servlet context has an isolated parent root context where beans common to all the application are available.

    The beans from the root context such as the session factory can be injected in the servlet context beans (such as the controllers), but not the other way around.

    The OpenSessionInViewFilter retrieves the session factory from the common root application context (applicationContext.xml) because it cannot know upfront in which servlet context to look.

    The code of OpenSessionInViewFilter calls lookupSessionFactory, which in turn ends up calling this code:

    /**
     * Find the root WebApplicationContext for this web application, which is
     * typically loaded via {@link org.springframework.web.context.ContextLoaderListener}.
     * <p>Will rethrow an exception that happened on root context startup,
     * to differentiate between a failed context startup and no context at all.
     * @param sc ServletContext to find the web application context for
     * @return the root WebApplicationContext for this web app, or {@code null} if none
     * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
     */
    public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    } 
    

    So this answers question A: OpenSessionInViewFilter needs to find the session factory in the root application context, and this explains why you need to move the session factory from the servlet context (dispatcher-servlet.xml) into the root context (applicationContext.xml).

    For question B, not all filters of the applictaion have this problem, this is specific of filters that need access to some spring bean and they need to know in which context to look.