Saturday, June 30, 2007

Spring And Hibernate

Spring can simplify your Hibernate application. Spring's Hibernate integration uses the same generic transaction infrastructure and DAO exception hierarchy that it uses for JDBC, JDO, iBATIS, and TopLink, making it easy to mix and match persistence methodologies if necessary.

There are two approaches to Spring's Hibernate integration:

  1. Inversion of Control with a HibernateTemplate and Callback
  2. Extending HibernateDaoSupport and Applying an AOP Interceptor

The IoC/HibernateTemplate methodology feels a lot like the JdbcTemplate methodology described in the last section. For this example, I will show the HibernateDaoSupport/AOP Interceptor approach.

After you have written your standard Hibernate mappings, there basically three things that you need to do to use Spring's HibernateDaoSupport to implement a DAO:

 

  1. Configure the Hibernate SessionFactory
  2. Extend your DAO Implementation from HibernateDaoSupport
  3. Wire in Transaction Support with AOP

So let's do another implementation of the WidgetDAO using Hibernate. First of all, here is our Hibernate XML mapping for the Widget class:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.sample.springrecipes.model">
  <class name="Widget" table="WIDGETS">
    <id name="id" column="WIDGET_ID" type="long">
      <generator class="native"/>
    </id>
    <property name="name" column="NAME" type="string"/>
    <property name="size" column="SIZE" type="int"/>
  </class>
</hibernate-mapping> 
 

If you've used Hibernate before, there should be nothing tricky here since Widget is a trivial class (there are no collections or other classes to associate to). So now we can wire up the Hibernate SessionFactory

Configuring the Hibernate SessionFactory in Spring

Here is an example Hibernate SessionFactory configured in Spring. You will be using this instead of the typical hibernate-config.xml. All we are doing here is telling Hibernate/Spring what our Hibernate mapping files are:

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="mappingResources">
    <list>
      <value>com/sample/springrecipes/base/Widget.hbm.xml</value>
    </list>
  </property>
</bean>

If you have a hibernate.properties in your classpath, the Spring LocalSessionFactoryBean will use that file to configure the database connections, dialect and pooling. Alternatively, you can define a DataSource in Spring (any class that implements javax.sql.DataSource) and explicity set all of your Hibernate properties in the LocalSessionFactoryBean:

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="mappingResources">
    <list>
      <value>com/sample/springrecipes/base/Widget.hbm.xml</value>
    </list>
  </property>
  <property name="hibernateProperties">
    <props>
      <prop key="hibernate.dialect">
        org.hibernate.dialect.HSQLDialect
      </prop>
    </props>
  </property>
  <property name="dataSource">
    <ref bean="dataSource"/>
  </property>
</bean>

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
    <value>org.hsqldb.jdbcDriver</value>
  </property>
  <property name="url">
    <value>jdbc:hsqldb:mem:widgets</value>
  </property>
  <property name="username"><value>sa</value></property>
  <property name="password"><value></value></property>
</bean>

Extending HibernateDaoSupport for the Actual DAO Implementation

The Spring HibernateDaoSupport class provides all kinds of convenience methods for working with Hibernate. Most of these are accessible via the HibernateTemplate object that this class exposes. Please note that Hibernate 2 throws checked HibernateExceptions and Hibernate 3 throws runtime exceptions. I am using Hibernate 3 and am blowing off runtime exceptions that might be thrown, but it might be more appropriate to deal with these exceptions here (this is just a example, not prduction code!) Anyway, here is our WidgetDAO implemented by extending HibernateDaoSupport:
public class WidgetDAOHibernateImpl 
              extends HibernateDaoSupport 
              implements WidgetDAO
{
  /**
   * Returns a java.util.List of all Widgets in the system.
   * @return
   */
  public Collection getWidgets()
  {
    return getHibernateTemplate().loadAll(Widget.class);
  }

  /**
   * Get a Widget Object given the id
   * @param id
   * @return
   */
  public Widget getWidgetById(Long id)
  {
    return (Widget)
            getHibernateTemplate().load(Widget.class, id); 
  }

  /**
   * Save a Widget Object, if the given Widget
   * is not in the data store,it should insert it,
   * if it is in the data store, it should update it.
   * @param widget
   */
  public Widget saveWidget(Widget widget)
  {
    getHibernateTemplate().saveOrUpdate(widget);
    return widget;
  }
}

Using AOP to Wire Up the DAO and Transaction Management

Now we wire up our DAO and weave in the HibernateInterceptor. This interceptor binds a new Hibernate Session to the thread before a method call, closing and removing it afterwards in case of any method outcome. If there already is a pre-bound Session, the interceptor simply participates in it. Note that the actual "widgetDAO" bean is defined as a proxy bean that really points to a bean called "widgetDaoTarget" which is where we wire up our actual implementation.
<!-- THE HIBERNATE INTERCEPTOR -->
<bean id="hibernateInterceptor"
class="org.springframework.orm.hibernate3.HibernateInterceptor">
  <property name="sessionFactory">
    <ref bean="sessionFactory"/>
  </property>
</bean>

<bean id="widgetDaoTarget"
class="com.sample.springrecipes.hibernate.WidgetDAOHibernateImpl">
  <property name="sessionFactory">
    <ref bean="sessionFactory"/>
  </property>
</bean>

<bean id="widgetDAO"
  class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces">
    <value>com.sample.springrecipes.base.WidgetDAO</value>
  </property>
  <property name="interceptorNames">
    <list>
      <value>hibernateInterceptor</value>
      <value>widgetDaoTarget</value>
    </list>
  </property>
</bean>

Summary

Spring can simplify your Hibernate code. Transactions can be wired in with AOP services provided by Spring.

5 comments:

KiLVaiDeN said...

Nice Article !

You can use org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator as well for simplicity, take a look at it !

Cheers
KiLVaiDeN

Paras Jain said...

Very good an useful article. Thanks.

Anonymous said...

You're a life saver, man.

-- Mike

BooM said...

Very Good Article!!!!... It has helped me so much!...

Before i didn't understand what the xmls of one project were doing there...

Thanks again.

Kranti said...

hi,
i am doing this in my web.xml

filter
filter-name>hibernateFilter filter-name>
filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter filter-class>
filter>


filter-mapping>
filter-name>hibernateFilter filter-name>
url-pattern>*.* url-pattern>
filter-mapping>

but it still desnt wrk fr me without lazy='false' for child objects

as per your article atleast we should work right?

and is there any issue with keeping the bean/object into http session?

the problem is i have user object and it has a child object called location.. means each user belongs to a location.

now during login i validate the user and keep the user object as well as the location object into the session

and in my jsp i do like locationObject.getLocationName()

this throws lazy exception..

infact exception is raised in java code itself when we try to do userObject.getLocation() to get location object before setting into http session