lishman levelup
«previous  next»


Getting Started
Form Processing
IoC Container
Hibernate ORM



Spring MVC and Hibernate Configuration


We will be creating a new file called hibernateDataAccessContext.xml, which contains all the necessary configuration details to allow Hibernate to integrate with Spring.

This gives us three configuration files which represent the three tiers in our application; world-servlet.xml (presentation), applicationContext.xml (business logic) and hibernateDataAccessContext.xml (data).

web.xml

First, we need to tell Spring where this file can be located. We change the contextConfigLocation context parameter in web.xml, to include the new file name.
<web-app>

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

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

  ..

</web-app>

Hibernate Configuration File

The hibernateDataAccessContext.xml file looks like this:
<beans>

  <!-- Auto-detect DAOs -->
  <context:component-scan base-package="levelup.world.dao.hibernate"/>

  <context:property-placeholder location="WEB-INF/jdbc.properties"/>

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


  <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
        p:dataSource-ref="dataSource"
        p:configurationClass="org.hibernate.cfg.AnnotationConfiguration"
        p:packagesToScan="levelup.world.domain">
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">${hibernate.dialect}</prop>
        <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
        <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
        <prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
      </props>
    </property>
    <property name="eventListeners">
      <map>
        <entry key="merge">
          <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
        </entry>
      </map>
    </property>

  </bean>


  <!-- Allow declarative transaction management using the @Transactional annotation -->
  <tx:annotation-driven transaction-manager="txnManager"/>

  <!-- Transaction manager for the Hibernate SessionFactory -->
  <bean id="txnManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager"
        p:sessionFactory-ref="sessionFactory"/>

  <!-- Apply persistence exception translation on @Repository classes -->
  <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

</beans>

DataSource

<context:property-placeholder> enables the replacement of ${..} placeholders with properties from the specified file.

The jdbc.properties file looks like this, for an Oracle 10g database:
jdbc.driverClassName=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@testserver:1521:test01
jdbc.username=scott
jdbc.password=tiger

dbcp.maxActive=100
dbcp.maxIdle=30
dbcp.maxWait=20000

hibernate.generate_statistics=true
hibernate.show_sql=true
hibernate.format_sql=false
hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
We use the properties from this file to provide several of the values for the dataSource and sessionFactory beans, such as database connection details, dbcp connection pool settings and Hibernate tracing options.

Tip
Tip - a properties file externalizes values that may change more frequently than the rest of the configuration.

SessionFactory

Our DAO implementation requires a Hibernate SessionFactory instance to be autowired by Spring. Here we configure the sessionFactory bean using Spring's AnnotationSessionFactoryBean, which is itself a factory for Hibernate session factories.

We use the packagesToScan bean property to tell Spring where to look for the Hibernate entities (ie classes annotated with @Entity). To manually specify the @Entity classes to be included, use the annotatedClasses property instead:
<bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
      p:dataSource-ref="dataSource"
      p:configurationClass="org.hibernate.cfg.AnnotationConfiguration"/>

  <property name="annotatedClasses">
    <list>
     <value>levelup.world.domain.Country</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.format_sql">${hibernate.format_sql}</prop>
      <prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
    </props>
  </property>
  <property name="eventListeners">
    <map>
      <entry key="merge">
        <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
      </entry>
    </map>
  </property>

</bean>

Merge Event Listener

The IdTransferringMergeEventListener event listener copies the identifiers of objects created with the Hibernate merge() method, back into the the original objects.

For example, without the IdTransferringMergeEventListener property, when a country is saved for the first time using this code:
  sessionFactory.getCurrentSession().merge(country);
  System.out.println("Country Id: " + country.getId());
The result is 'Country Id: null'. However, when IdTransferringMergeEventListener is included, the result will be like this: 'Country Id: 1104'.

Transactions

<tx:annotation-driven/> enables declarative transaction management with the @Transactional annotation.

The txnManager bean tells Spring to use the HibernateTransactionManager class, which delegates responsibility for transaction management to Hibernate itself.

Tip
Tip - if the bean name for the transaction manager is 'transactionManager' then the transaction-manager attribute of <tx:annotation-driven/> is not required, as this is the default.

Exception Handling

PersistenceExceptionTranslationPostProcessor automatically applies persistence exception translation to any bean that carries the @Repository annotation. This means that exceptions thrown from Hibernate are translated into Spring's DataAccessException hierarchy . This allows data access exceptions to be abstracted away from the data layer so that they remain consistent, regardless of the persistence technology.
»