Tuesday 1 February 2011

Spring Hibernate Integration

In this article, I will be demonstrating how you can integrate hibernate with your Spring application. We will not be discussing hibernate and Spring in any detail. The focus is to get you started with an Spring application that persists data into a database using the hibernate ORM layer.

I will safely assume that you are familiar with Spring, hibernate, SQL, maven. For the purpose of this demo, i will be using the MySQL database, STS (SpringSource Tool Suite) or Eclipse with Spring support plugins installed. If you are not using any IDE, you just need to create some folders and config files yourself. But i see no reason why you shouldn't since it will really speed your development process.
(find the source code of the sample application at the end of the page)


So Let's get started!


 STEP 1: Create a Spring hibernate Utility project I am using STS for creating my project; STS is a customized eclipse environment. You can use your favorite eclipse installation, for sake of ease, install the Spring plugins and you're ready to go. If you are going the manual way, nothing to worry about. Just a few folders and config files to be created.

Choose the 'Spring Template Project' from the File -> New menu or the dashboard.

From the Template Selection dialog, choose 'Simple Spring Hibernate Utility Project'


Type in a project name and a base package name ( eg. EStore and com.test.estore respectively) and press Finish. You will see that the following code structure has been generated for you


A few configuration files will be genereated here. You will find a pom.xml at the base folder of the project. This is where we will add the maven dependencies in a short while. You will also find the app-context.xml which is the base spring config file at /src/main/resources/META-INF/spring. Any property files needed for the project can be placed inside /src/main/resources/META-INF/properties.

The maven configuration file - pom.xml will already have all the dependencies for using Spring and hibernate. Since we are using the mysql database, we will need to add the following dependency to use the mysql connector:


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.12</version>
        </dependency>
If you are not using the IDE, you will have to add a couple of dependencies to your pom.xml. Please refer the pom.xml in the source code attached at the end of the article.

STEP 2: Create a domain class

We will create a simple domain class called Product in a seperate package 'com.test.estore.model'. Each domain class maps to a table in the database. Let's take a look at the code and i then walk through the code.
@Entity
@Table(name = "Product")

public class Product {

    private Integer productId;
    private String productCode;
    private String name;
    private String description;
    private Double price;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")

    public Integer getProductId() {
        return productId;
    }
    public void setProductId(Integer productId) {
        this.productId = productId;
    }

    @Column(name = "NAME", nullable = false, length = 50)
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Column(name = "CODE", nullable = false, unique = true, length = 20)
    public String getProductCode() {
        return productCode;
    }
    public void setProductCode(String productCode) {
        this.productCode = productCode;
    }

    @Column(name = "DESCRIPTION")
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }

    @Column(name = "PRICE")
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }

}
As you see, Product is a very simple POJO with a few additional annotations. The @Entity annotation specifies that this class is persistent and the @Table annotation specifies the name of the table to which it will be mapped. Please note that the @Table annotation is not compulsory.

The @Column annotation specifies the name of the column to be mapped with the field in the class. Again, it is NOT necessary to specify the @Column annotation unless you want the column name to be different than the field name. Every field in the class will be by default persisted into a column of the table, unless specifically annotated with the @Transient annotation. In addition to the name, you can also specify whether the column is nullable, unique, length restrictions etc.

The @Id annotation specified the identity property of the class. In hibernate, every table needs to have this unique key. There are different strategies available for generating the values for this field. In this case, we are using GenrationType.Auto.

These annotations can be added over the fields of the getters/setters. The placement of the @Id annotation determines which strategy will be used. In this particular case for eg, since the @Id annotation is placed over the property getter/setter, the property access strategy is used instead of the field access.
(Field access means: hibernate will access the field directly using reflection.
 Property access: hibernate will use the get/set pair to access the value - property access is the default)

Let's now move to the next step and create the data access layer.

STEP 3: Create the DAO layer
Now that our model class is ready, let's create the DAO interface and it's implementation in a package say : 'com.test.estore.dao'
IProductDAO.java
package com.test.estore.dao;
import com.test.estore.model.Product;

public interface IProductDAO {
    public void save(Product product);
    public void update(Product product);
    public void delete(Product product);
    public Product findByProductCode(String code);
}
 ProductDAO.java
package com.test.estore.dao;
 /*import statements*/
@Repository("productDao")
public class ProductDAO implements IProductDAO {

    private HibernateTemplate hibernateTemplate;

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        hibernateTemplate = new HibernateTemplate(sessionFactory);
    }
    public void save(Product product) {
        hibernateTemplate.save(product);
    }
    public void update(Product product) {
        hibernateTemplate.update(product);
    }
    public void delete(Product product) {
        hibernateTemplate.delete(product);
    }
    public Product findByProductCode(String code) {
        List products = hibernateTemplate.find(
                "from Product where productCode = ?", code);
        return (Product) products.get(0);
    }
}
The ProductDAO class is quite self explanatory, there're just two points worth writing about.
1. Notice the @Repository annotation on the ProductDAO class. @Repository is a specialized @Component type. @Repository indicates that the class acts as a repository or a data access object. This enables transparent exception translation. And it also provides a more clear scope for applying a pointcut or an aspect for future support.

The @Autowired annotation indicates that the sessionFactory bean is injected into the ProductDAO by the spring container.

STEP 4: Create the service layer
Now that we have the DAO layer in place, it's time to setup the service layer. The service layer is pretty straightforward:
IProductService.java
package com.test.estore.service;
import com.test.estore.model.Product;

public interface IProductService {
    public void save(Product product);
    public void update(Product product);
    public void delete(Product product);
    public Product findByProductCode(String code);
}
ProductService.java
package com.test.estore.service;

/* import statements */ ....

@Service("productService")
public class ProductService {

        @Autowired
        ProductDAO productDAO;
   
        public void save(Product product){
            System.out.println("save called");
            productDAO.save(product);
        }
        public void update(Product product){
            productDAO.update(product);
        }
        public void delete(Product product){
            productDAO.delete(product);
        }
        public Product findByProductCode(String code)
        {
            return productDAO.findByProductCode(code);
        }
}
STEP 5: Configuration files
The most important configuration file in this application is the base Spring context xml. For the sake of clarity, we will place all the hibernate related configuration in a separate configuration file - hibernate.xml. We will define the datasource bean and the sessionFactory bean in hibernate.xml. We will be storing the database details in a property file and read the property file in the context xml. Let's take a look at these configs:


application.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_hibernate
jdbc.username=root
jdbc.password=password
 hibernate.xml
<?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.5.xsd">

    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>application.properties</value>
        </property>
    </bean>

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>com.test.estore.model.Product</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
            </props>
        </property>
    </bean>
</beans>
In the above config, we read the database details from the property file and create the dataSource bean. Here we don't need any hibernate.cfg.xml file, we declare the hibernate sessionFactory bean in the xml file to which we pass the dataSource bean. We also list down the list of all the 'annotatedClasses' in the application i.e. all the model classes (just like we specify the mapping files in the hibernate.cfg.xml file).
Also in this case, we want to create the table when we run the application the first time, and hence the value of 'hibernate.hbm2ddl.auto' has been set to 'create'. This is all we need to do to set up the configs for our database.

Next we need to setup the core spring config - app-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <import resource="hibernate.xml"/>
    <context:component-scan base-package="com.test.estore" />

</beans>
The first line imports the hibernate config file. Also, we need to tell the Spring container that it needs to look for annotated classes. To do this we need to use the component-scan tag and pass to it the base package - this is where the container starts it's search from.

STEP 6: Write the test application
Now that we're done with almost everything, we need to test the application. All we need is a simple class with the main method that loads the spring context and tries to persist a Product into the database
package com.test.estore.app;
/* import statements */ ..

public class RunEStore {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");

        ProductService productService = (ProductService) ctx.getBean("productService");
        Product product1 = new Product();

        product1.setName("maggie noodles");
        product1.setDescription("noodles made out of flour");
        product1.setProductCode("MAGG_NOODLES");
        product1.setPrice(30D);

        productService.save(product1);

        System.out.println("Product saved: "
                + productService.findByProductCode("MAGG_NOODLES").getName());
    }
}
So, we're ready to go.. wait , not yet!

STEP 7: Where's the database?

We first need to create the 'spring_hibernate' schema that we specified in the application.properties file :

Now we're ready to go. Run the RunEStore class, a table called 'Product' would be created and a row would be added to the table.

This was a very very basic example. You would mostly use hibernate in a Spring MVC project; you need to follow the same steps, just a few configurations would need to change and additions to the pom.xml file would be needed.
There's a lot more to Spring hibernate such as exception handling, one-to-many, many-to-many relationships. Another article, another day.

Hope you found this demo useful.
Happy coding!


Download the sample application : EStore.rar

4 comments:

  1. Great article! Thank you.
    I guess the line
    "public class ProductService {"
    was meant to be
    "public class ProductService implements IProductService {"
    right? Otherwise what is the purpose of the interface IProductService?

    msenocak

    ReplyDelete
  2. In STEP5, first paragraph you wrote:
    "We will be storing the database details in a property file and read the property file in the context xml."
    I guess you meant "... and read the property file in hibernate.xml."
    Right?

    ReplyDelete
  3. Hello

    I'm getting the following error when starting the application:

    Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [app-context.xml]; nested exception is java.io.FileNotFoundException: class path resource [app-context.xml] cannot be opened because it does not exist

    What am I doing wrong?

    ReplyDelete
  4. Every configuration file should be under src folder or create a folder under src say resources and place all your configuration files into that folder.
    When ever you are calling those files at "RunEStore" class, make sure you are calling from those resource file, if you kept all these resource files under src folder then the above code works with out any problem.

    ReplyDelete