Wednesday, May 28, 2014

Spring-integrated Hibernate 3 to Hibernate 4 Upgrade

I recently upgraded a large Java web application from Hibernate 3.6.6 to 4.3.5. Given the level of Spring and Hibernate integration in this application, I also upgraded Spring from 3.2.2 to 4.0.5 at the same time.

This was a relatively painless major upgrade. The application configuration changes were pretty straightforward and the code changes were minimal.

Here are the changes required for this particular upgrade:

Dependency Changes


Out with the old:
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>3.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>3.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>3.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>3.6.6.Final</version>
    </dependency>
    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache-core</artifactId>
      <version>2.4.3</version>
    </dependency>

In with the new:

  <properties>
    . . .
    <hibernate.version>4.3.5.Final</hibernate.version>
    <spring.version>4.0.5.RELEASE</spring.version>
  </properties>  
  . . .
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-ehcache</artifactId>
      <version>${hibernate.version}</version>
    </dependency>

Incompatible Spring Configuration


This application has both XML and Annotation-based spring configuration. While Spring 4 is largely backward compatible, we had one XML config for custom property editors that needed a change.

The old CustomEditorConfigurer took in singleton instances:

<bean id="com.joshlandin.common.util.bean.ConditionEditor"
   class="com.joshlandin.common.util.bean.ConditionEditor"/>
<bean id="com.joshlandin.common.util.bean.JoinEditor"
   class="com.joshlandin.common.util.bean.JoinEditor"/>

<bean id="customEditorConfigurer" 
  class="org.springframework.beans.factory.config.CustomEditorConfigurer">
  <property name="customEditors">
    <map>
      <entry key="com.joshlandin.common.sql.Condition"><ref bean="com.joshlandin.common.util.bean.ConditionEditor"/></entry>
      <entry key="com.joshlandin.common.sql.Join"><ref bean="com.joshlandin.common.util.bean.JoinEditor"/></entry>
    </map>
  </property>
</bean>

The new one takes in class names (that are converted to Class objects by spring):

<bean id="customEditorConfigurer" 
  class="org.springframework.beans.factory.config.CustomEditorConfigurer">
  <property name="customEditors">
    <map>
      <entry key="com.joshlandin.common.sql.Condition" value="com.joshlandin.common.util.bean.ConditionEditor"/>
      <entry key="com.joshlandin.common.sql.Join"      value="com.joshlandin.common.util.bean.JoinEditor"/>
    </map>
  </property>
</bean>

Deprecated Hibernate Fields Removed


All Hibernate types were removed from org.hibernate.Hibernate and replaced with singleton instances under the org.hibernate.type package. We had a few references to these in areas of the application using DetachedCriteria with SQL restrictions.

Old:
 criteria.add(Restrictions.sqlRestriction(SQLSyntax.truncate("amount", d) + " = ?", 
    form.propertyValue("searchAmount"), Hibernate.BIG_DECIMAL));

New:
 criteria.add(Restrictions.sqlRestriction(SQLSyntax.truncate("amount", d) + " = ?", 
    form.propertyValue("searchAmount"), BigDecimalType.INSTANCE));

Hibernate Session Factory Changes


There are some class changes that impact the Hibernate Session Factory configuration. The new LocalSessionFactoryBean now provides built-in support for annotation-based mappings so the configurationClass property has been removed. Additionally, the ehcache classes have been repackaged into Hibernate requiring a change to the region factory class name. Spring also provides complementary hibernate4 packages for existing hibernate3 classes.

Old:

  <bean id="commonSessionFactoryProperties" abstract="true">
    <property name="configurationClass">
      <value>org.hibernate.cfg.AnnotationConfiguration</value>
    </property>
    . . .
    <property name="annotatedClasses">
      <list>
      . . .
      </list>
    </property>
    <property name="mappingResources">
      <list>
      . . .
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
      . . .
        <prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory</prop>
      </props>
    </property>
  </bean>

   <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" parent="commonSessionFactoryProperties">
    <property name="dataSource">
      <ref bean="javax.sql.DataSource.rw"/>
    </property>
  </bean>
  
  <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory"><ref local="sessionFactory"/></property>
  </bean>  
    
  <bean id="org.hibernate.SessionFactory.wq" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" parent="commonSessionFactoryProperties">
    <property name="dataSource">
      <ref bean="javax.sql.DataSource.wq"/>
    </property>
  </bean>

  <bean id="com.joshlandin.common.domain.persistence.dao.DataAccessObject.wq"
    class="com.joshlandin.common.domain.persistence.dao.DataAccessObject" scope="singleton">
    <property name="sessionFactory"><ref bean="org.hibernate.SessionFactory.wq"/></property>
  </bean>

New:

 <bean id="commonSessionFactoryProperties" abstract="true">
    <property name="annotatedClasses">
      <list>
      . . .
      </list>
    </property>
    <property name="mappingResources">
      <list>
      . . .
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
        . . . 
        <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
      </props>
    </property>
  </bean> 

  <bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"  
    parent="commonSessionFactoryProperties">
    <property name="dataSource">
      <ref bean="javax.sql.DataSource.rw"/>
    </property>
  </bean>
  
  <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory"><ref local="sessionFactory"/></property>
  </bean>  
    
  <bean id="org.hibernate.SessionFactory.wq"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
    parent="commonSessionFactoryProperties">
    <property name="dataSource">
      <ref bean="javax.sql.DataSource.wq"/>
    </property>
  </bean>

Servlet Filter Changes


This application uses the OpenSessionInViewFilter configured in the web.xml. The package name had to be changed from hibernate3 to hibernate4:

  org.springframework.orm.hibernate4.support.OpenSessionInViewFilter

All Done


That's it. No other changes were required for this application (though your app's mileage may vary).

If only this upgrade would have fixed the Hibernate optimistic locking issue with MySQL 5.6 sub-second precision.

You can find more Spring 3 to 4 upgrade information here and Hibernate 3 to 4 upgrade information here.

No comments:

Post a Comment