Search This Blog

Thursday 20 September 2012

Spring and Hibernate

In our earlier post we saw how Spring can be integrated with JDBC. Spring also provides a similar template class for use with Hibernate - org.springframework.orm.hibernate3.HibernateTemplate.
The class provides us with the following main advantages:
  1. Converts HibernateExceptions into DataAccessExceptions
  2. Provides Hibernate Session handling such as retrieving/closing Hibernate Sessions.
  3. Its capability to fall back to 'auto-commit' style behavior when used outside of transactions.
  4. Provides a host of convenience methods
With the arrival of the  @Repository annotation, point 1 is taken care of. In Hibernate 3.0.1 the getCurrentSession() method has been introduced which ensures that developers do not have to get into the nitty gritty of session management. In fact the Spring documentation itself encourages the direct use of Hibernate interfaces like sessionFactory and to skip this template completely.
I decided to implement the IPersonDAO using this template.
public class PersonTemplateDAO implements IPersonDAO {
    
    private HibernateTemplate hibernateTemplate;
    
    public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }

    public final HibernateTemplate getHibernateTemplate() {
      return this.hibernateTemplate;
    }
//other methods
}
In the above case we need to create the HbernateTemplate bean and wire it into our DAO class. The other option is to extend the DAO Support class provided by Spring.
public class PersonTemplateDAO extends HibernateDaoSupport implements IPersonDAO {
In this case we can either directly wire the class with  HibernateTemplate bean (like above) or we wire the class with a sessionFactory. The DAO Support class will then automatically create the Hibernate Template internally.
<bean id="PersonDAO" class="com.data.dao.PersonTemplateDAO" >
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
The SessionFactory implementation we use here is a Spring implementation.
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <!-- The dataSource property is wired with a reference to a DataSource bean. -->
    <property name="dataSource" ref="c3pDataSource" /> 
    <!-- The mappingResources property lists one or more Hibernate mappings  -->
    <property name="mappingResources">
        <list>
            <value>com/data/model/Person.hbm.xml </value>
        </list>
    </property>
    <!-- hibernateProperties is where we configure the details of Hibernate working --> 
    <property name="hibernateProperties">
        <props>
            <prop key="dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="show_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">create</prop>
        </props>
    </property>
</bean
This is an example of a FactoryBean. The above implementation is used if we are working with hbm files.The object takes a reference to the data source in operation.
The hbm file and the Model class is as below:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.data.model">
    <class name="Person">
        <meta attribute="class-description">This is a person table</meta>

        <id name="id" type="long" column="ID">
            <generator class="identity" />
        </id>

        <property name="name" type="string" column="NAME" />
        <property name="age" type="integer" column="AGE" />
    </class>

</hibernate-mapping>
The Model class:
public class Person {
    private String name;
    private Long id;
    private Integer age;
//getter, setters
}
The DAO class is as below:
public class PersonTemplateDAO extends HibernateDaoSupport implements
        IPersonDAO {

    private static final Logger logger = Logger
            .getLogger(PersonTemplateDAO.class);

    @SuppressWarnings("unchecked")
    @Override
    public List<Person> getAllPersons() {
        logger.debug("getAllPersons from system");
        List<Person> persons = new ArrayList<Person>(0);
        persons = this.getHibernateTemplate().find("from Person");
        logger.debug("Total Retrieved items : " + persons.size());
        return persons;
    }

    @Override
    public int findTotalPersons() {
        logger.debug("findTotalPersons: fetching record count ");
        return this.getHibernateTemplate().find("from PERSON").size();
    }

    @Override
    public Person getPersonById(final long personId) {
        logger.debug("fetching record with  id : " + personId);
        return (Person) this.getHibernateTemplate()
                .get(Person.class, personId);

    }

    @Override
    public void updatePerson(final Person person) {
        this.getHibernateTemplate().update(person);
    }

    @Override
    public void save(Person person) {
        this.getHibernateTemplate().save(person);
    }

    @Override
    public void delete(Person person) {
        this.getHibernateTemplate().delete(person);
    }

}
In the above code there is no open session or close session code. All that is internally manged by the HibernateTemplate. We in fact have a reduced need to work with Hibernate directly using the Hibernate API. We have the find method that can work with HQL. Also the template exposes utility methods for save, get, delete etc.
Note: In the above code no transaction support has been added as such. I have been dependent on the auto-commit behavior provided by the HibernateTemplate So if a load call were to be made then I would end with a lazy load exception.
Also there is an additional setting in the sessionFactory implementation that I came across. Certain blogs advised setting it to false.
<property name="exposeTransactionAwareSessionFactory"><value>false</value></property>
I wen through the Spring code and came across the documentation for the setter method for above property:
Set whether to expose a transaction-aware current Session from the
SessionFactory's <code>getCurrentSession()</code> method, returning the
Session that's associated with the current Spring-managed transaction, if any.
<p>Default is "true", letting data access code work with the plain
Hibernate SessionFactory and its <code>getCurrentSession()</code> method,
while still being able to participate in current Spring-managed transactions:
with any transaction management strategy, either local or JTA / EJB CMT,
and any transaction synchronization mechanism, either Spring or JTA.
Furthermore, <code>getCurrentSession()</code> will also seamlessly work with
a request-scoped Session managed by OpenSessionInViewFilter/Interceptor.
<p>Turn this flag off to expose the plain Hibernate SessionFactory with
Hibernate's default <code>getCurrentSession()</code> behavior, supporting
plain JTA synchronization only. Alternatively, simply override the
corresponding Hibernate property "hibernate.current_session_context_class".
I did not use it nor did I use the current_session_context_class property.
<prop key="current_session_context_class">thread</prop>

The code to test the DAO is as follows:
public static void main(String[] args) {
    //final XmlBeanFactory beanFactory = 
    //new XmlBeanFactory(new ClassPathResource("spring-hib-template.xml"));
    final ApplicationContext beanFactory = 
            new ClassPathXmlApplicationContext("spring-hib-template.xml");    
    IPersonDAO personDAO = (IPersonDAO) beanFactory.getBean("personDAO");
    System.out.println(personDAO);
    testPersonLifeCycle(personDAO);
}
public static void testPersonLifeCycle(final IPersonDAO personDAO) {
    Person person = new Person();
    person.setName("Vinod");
    person.setAge(26);
    personDAO.save(person);
    final Long personId = person.getId();
    System.out.println("Person was saved with id " + personId);
    person = personDAO.getPersonById(personId);
    person.setName("Raja");
    personDAO.updatePerson(person);
    System.out.println("Person with id " +personId + " was updated");
    
    person = personDAO.getPersonById(personId);
    System.out.println("Person with id " + personId + " is " 
             + person.getName() + " and age is " + person.getAge());
    personDAO.delete(person);
    System.out.println("person with id " + personId 
             + " has been deleted successfully");
}
In the next post we shall used at the new and recommended style of integrating Spring and Hibernate.

1 comment:

  1. Thanks Robin very nice blog. nice explanation.

    can you explain like how to pass parameter in getHibernateTemplate

    for example : how to add 2-3 joins in query in getHibernateTemplate().find method.

    ReplyDelete