Pages

Sunday, August 1, 2010

Spring With Hibernate


Spring provides hibernate template and it has many advantages like
  • It removes boiler plate code like getting connection from data source try/catch block for closing connection. So that developer can focus on writing business logic rather then writing boilier plate code every where.
  • Spring hibernateTemplate also throws RunTime exception compared to checkd exception which allows to remove writing try/catch block in each DAO. 
  • It also gives richer template class using which developer can write query code easily. This template class also allows to get session explicitly so if developer wants to get session object and work on it then it's possible.
  • Spring provides support for both hibernate and JDBC Templates.It provides template classes which contains all common code.But JDBC as we all know is not an ORM tool it does not represent rows as objects whereas Hibernate does that.
  • Anyway, sometimes you will need to access directly hibernate objects. For example, Query object has some interesting methods, not accessible in the templete. For such cases, you may use getHibernateTemplate().execute(HibernateCallBack).
  • HibernateCallBack, is just an interface which has a method which will recieve an Hibernate Session object you can operate with (Normally, you will create an inner class)
  • A final advice. If you use some callback function very frequently, just extend the HibernateTemplate, and make a wrapping method for the callback. It is much cleaner than using callbacks everywhere
Spring With Hibernate
As a pre-requisite, let us understand the need for such integration before we actually get into the integration between these two technologies. It is well known that Hibernate is a powerful ORM tool that lies between Application and Database. It enables Application to access data from any database in a platform-independent manner. There is no need for the Application to depend on the low-level JDBC details like managing connection, dealing with statements and result sets. All the necessary details for accessing a particular data source is easily configurable in Xml files. Another good thing is that Hibernate can be coupled well with both J2SE and J2EE Applications.

One of the problem with using Hibernate is that the client Application that accesses the database using Hibernate Framework has to depend on the Hibernate APIs like Configuration, SessionFactory and Session. These objects will continue to get scattered across the code throughout the Application. Moreover, the Application code has to manually maintain and manage these objects. In the case of Spring, the business objects can be highly configurable with the help of IOC Container. In simple words, the state of an object can be externalized from the Application code. It means that now it is possible to use the Hibernate objects as Spring Beans and they can enjoy all the facilities that Spring provides.

Creating Database
 CREATE TABLE `customer` (
  `ID` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `NAME` varchar(100) NOT NULL,
  `AGE` int(10) UNSIGNED NOT NULL,
  PRIMARY KEY (`CUST_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Customer.java
Now let us create a class called Customer for storing the data that are fetched from the Customer table. The class design is such that the column names for the table 'Customer' will be mapped as the variable names in the Java class with the appropriate data type. The complete code listing for the Customer class is as follows,
package com;

public class Customer{
private int id;
private String name;
private int age;
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
}

customer.hbm.xml

We have created 'customer' table in the database and a corresponding Java class in the Application layer. However, we haven't specified that the 'customer' table should map to the Java class and the column names in the 'customer' table should map to the Java variables in the Customer class. This is where the Hibernate Mapping files comes into picture. Let us have a look at the Hibernate Mapping file,

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="com.Customer" table="CUSTOMER" schema="MYBLOG">
        <id name="id" type="java.lang.Long">
            <column name="ID" />
            <generator class="assigned"></generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" length="20" />
        </property>
        <property name="age" type="java.lang.Long">
            <column name="AGE" />
        </property>
    </class>
</hibernate-mapping>

Note that the Mapping file is an Xml file and its name is Customer.hbm.xml. The portion of the string 'hbm' in the mapping file stands for hibernate Mapping File. Although it is not necessary to follow this convention, it will be easy to figure what type of xml file is this, just by looking at the extension. Xml conforms to a well-defined DTD, the hibernate-mappings-3.0.dtd.

The root element for the mapping file is the hibernate-mapping tag which can define one or more mappings, following which we have the class tag which defines a mapping between the database table name and the Java class. The 'name' attribute must point to a fully qualified Java class name whereas the table attribte must point to the database table.

The next series of tags define the mapping definition of the column names against its Java variables counterparts. The 'id' tag defines an identifier for a row and it is commonly used as a primary key column. The property tag has an attribute called 'name' which points to the Java variable name, following which is the name of the column in the database table to which it maps to.

applicationContext.xml
This section deals with configuring the various information needed for the Spring Framework. In Spring, all the business objects are configured in Xml file and the configured business objects are called Spring Beans. These Spring Beans are maintained by the IOC which is given to the Client Application upon request. Let us define a data source as follows,

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" >
    <property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"/>
    <property name="url" value="jdbc:derby://localhost:1527/myeclipse"/>
    <property name="username" value="myblog"/>
    <property name="password" value="myblog"/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="mappingResources">
        <list>
            <value>/com/Customer.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
              <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop>
              <prop key="hbm2ddl.auto">update</prop>
              <prop key="show_sql">true</prop>
         </props>
    </property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
    <property name="sessionFactory">
        <ref bean="mySessionFactory"/>
    </property>
</bean>
<bean id="customerDAO" class="com.CustomerDAOImpl">
<property name="hibernateTemplate">
        <ref bean="hibernateTemplate"/>    
    </property>
</bean>
</beans>

1. The above First bean defines a data-source of type 'org.apache.commons.dbcp.BasicDataSource'. More importantly, it defines the various connection properties that are needed for accessing the database. For accessing the Derby database, we need Derby database driver derbyclient.jar
  • The first property called driverClassName should point to the class name of the Derby Database Driver.
  • The second property url represents the URL string which is needed to connect to the Derby Database. 
  • The third and the fourth properties represent the database username and the password needed to open up a database session.
2. Now, let us define the second Spring Bean which is the SessionFactoryBean. If you would have programmed in Hibernate, you will realize that SessionFactoryBean is responsible for creating Session objects through which Transaction and Data accessing is done. Now the same SessionFactoryBean has to be configured in Spring's way as follows,

<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="mappingResources">
        <list>
            <value>/com/Customer.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
              <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop>
              <prop key="hbm2ddl.auto">update</prop>
              <prop key="show_sql">true</prop>
         </props>
    </property>
</bean>

To make the SessionFactoryBean to get properly configured, we have given two mandatory information. One is the data-source information which contains the details for accessing the database. This we have configured already in the previous step and have referred it here using the 'ref' attribute in the 'property' tag. The second one is a list of Mapping files which contains the mapping information between the database tables and the Java class names. We have defined one such mapping file in section 2 and have referenced the same here with the 'list' tag.

3.The 3rd important Spring Bean is the Hibernate Template. It provides a wrapper for low-level data accessing and manipulation. Precisely, it contains methods for inserting/delting/updating/finding data in the database. For the Hibernate Template to get configured, the only argument is the SessionFactoryBean object as represented in the following section,

<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
    <property name="sessionFactory">
        <ref bean="mySessionFactory"/>
    </property>
</bean>

4.The final Bean definition is the Dao class which is the client facing class. Since this class has to be defined in the Application level, it can contain any number of methods for wrapping data access to the Client. Since we know that it is the Hibernate Template class that interacts with the database, it will be ideal a refer an instance of Hibernate Template to the Dao class

<bean id="customerDAO" class="com.CustomerDAOImpl">
<property name="hibernateTemplate">
        <ref bean="hibernateTemplate"/>    
    </property>
</bean>


CusomerDAOImpl.java

As described earlier, this CustomerDaoImpl class can contain any number of methods that can be accessed by the clients. The design of this class can fall under two choices. One is this class can directly depend on the Hibernate Template object which is injected by the IOC for accessing the data. The second one is that it can make use of the Hibernate API for data accessing. The declaration of the class is as follows,
package com;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class CustomerDAOImpl extends HibernateDaoSupport {

public Customer findByCustomerId(int custId) {
// TODO Auto-generated method stub
return (Customer)getHibernateTemplate().get(Customer.class, new Integer(custId));
}
public void insert(Customer customer) {
// TODO Auto-generated method stub
getHibernateTemplate().saveOrUpdate(customer);
}
}

Test.java
package com;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context =
     new ClassPathXmlApplicationContext("/applicationContext.xml");
        CustomerDAOImpl customerDAO = (CustomerDAOImpl) context.getBean("customerDAO");
        Customer customer = new Customer(3, "Amith",29);
        customerDAO.insert(customer);
        Customer customer1 = customerDAO.findByCustomerId(2);
        System.out.println(" ::: Customer Record From DataBase ::: ");
        System.out.println(" Customer ID ::: "+customer1.getId());
        System.out.println(" Customer Name ::: "+customer1.getName());
        System.out.println(" Customer Age ::: "+customer1.getAge());
}
}

Finally, we come to the sample client Application for accessing the test data. The control goes like this. When the method BeanFactory.getBean("customerDao") is called, Spring finds the references made in the Bean definition of CustomerDaoImpl Bean. It happens to be the Hibernate Template object. Then an attempt will be made to initialize the Hibernate Template object where it will see that a Session Factory Bean object is referenced. Then, while constructing the Session Factory Bean object, the data-source information will get resolved along with the database tables and the Java classes.


Note *** To run the below example you need below jar files in your class path

  • derbyclient.jar
  • spring-dao.jar
  • spring-jdbc.jar
  • hibernate3.jar
  • jta.jar
  • commons-collections-2.1.1.jar
  • commons-dbcp.jar
  • spring-hibernate3.jar

No comments:

Post a Comment