Jan 3, 2014

Spring 4 integration with Hibernate 4

Step 1 Download Hibernate 4 binaries from 
http://hibernate.org/downloads/ 
and Spring binaries using following maven file.

<dependencies>
     <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>4.0.0.RELEASE</version>
     </dependency>
     <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-orm</artifactId>
           <version>4.0.0.RELEASE</version>
     </dependency>
     <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-jms</artifactId>
           <version>4.0.0.RELEASE</version>
     </dependency>
</dependencies>

Step 2 Create a configuration file (if you are using eclipse create this file in src folder and if this is web application it can go anywhere in WEB-INF but need to configure the same in web.xml ) to tell Spring context factory what packages it need to look for beans configured using annotations. Here we will use 2 different annotations to distinguish between service layer, DAO layer (if this would have been a web application we have user third annotation @Controller). We will be using  annotation @Service for service class and @Component for DAO class. Remember all these annotations can be used interchangeably.
Also we will create a bean tag for configuring hibernate session factory and pass connection information to this session factory by creating another bean called dataSource. How to use this session factory that we will see in later steps but notice that we need to use LocalSessionFactoryBean from hibernate4 package and not from hibernate3 package. Here is an example of my configuration file spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      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-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd">
   
    <!-- This sescion tells Spring context where to look for bean classes with appropriate annotations -->
      <context:component-scan base-package="com.techcielo.spring4"></context:component-scan>

      <!-- Configure JDBC Connection-->
      <bean id="dataSource"
            class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost:3306/northwind" />
            <property name="username" value="root" />
            <property name="password" value="" />
      </bean>

      <!-- Configure Hibernate 4 Session Facotry -->
      <bean id="sessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

            <property name="dataSource">
                  <ref bean="dataSource" />
            </property>

            <property name="hibernateProperties">
                  <props>
                        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                        <prop key="hibernate.show_sql">true</prop>
                  </props>
            </property>
      </bean>
</beans>

So we are ready with basic configuration and we can now create service class, DAO class and one Main class to test if this code is working or not. Following is our DAO class in com.techcielo.spring4.dao package (com.techcielo.spring4 is the package that spring container will check for classes with annotations)

package com.techcielo.spring4.dao;

import org.springframework.stereotype.Component;

import com.techcielo.spring4.bean.Product;

@Component("prodDAO")//This the id by which it will be auto wired
public class ProductDAO extends BaseDAO{
             
       public Product getProduct(int id){
              System.out.println("In DAO class");
              return null;
       }
}

Notice that we have given annotation @Component and provided id prodDAO by which Spring container will wire this class to other classes.
We will use BaseDAO to store information about Hibernate SessionFactory information configured in our xml file, but for now we will focus on injecting this class in service class ProductService as shown below.

package com.techcielo.spring4.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.techcielo.spring4.bean.Product;
import com.techcielo.spring4.dao.ProductDAO;

@Service("prodSvc")
public class ProductService {
      
       @Autowired
       private ProductDAO prodDAO;
       public void setProdDAO(ProductDAO prodDAO) {
              this.prodDAO = prodDAO;
       }
      
       public Product getProduct(int id){
              System.out.println("In Service class...will call DAO");
              return prodDAO.getProduct(id);
       }
}

Here our service class is identified by annotation @Service and is given id prodSvc, also bean identified by prodDAO is attached to it by annotation @Autowired. Now we will write a class to test if this code is working or not. So following class will get an instance of service class and will call method getProduct of this instance.

package com.techcielo.spring4.main;

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

import com.techcielo.spring4.service.ProductService;

public class Main {
       public static void main(String[] args)
       {
              //Build application context by reading spring-config.xml
              ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"spring-config.xml"});
             
              //Get an instance of ProductService class;
              ProductService prdSvc = (ProductService) ctx.getBean("prodSvc");
             
              //Call getProduct method of ProductService
              prdSvc.getProduct(1);
       }
}

When we run this class following will be output.

Jan 03, 2014 1:24:51 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3caa4b: startup date [Fri Jan 03 13:24:51 IST 2014]; root of context hierarchy
Jan 03, 2014 1:24:51 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Jan 03, 2014 1:24:52 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Jan 03, 2014 1:24:52 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jan 03, 2014 1:24:52 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.7.Final}
Jan 03, 2014 1:24:52 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jan 03, 2014 1:24:52 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jan 03, 2014 1:24:53 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
Jan 03, 2014 1:24:53 PM org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [3] less than 4
Jan 03, 2014 1:24:53 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
Jan 03, 2014 1:24:53 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
In Service class...will call DAO
In DAO class

Now we will actually hit the DB and get the details about entiry Product and will print the same in our Main class. In order to do this we will use openly available database called NorthWind. (Going forward to understand different relationships in hibernate we will be using same database). 


Database with more than what is mentioned in above diagram is available at following location


Step 3 As mentioned in last step now we will use BaseDAO to store information about Hibernate SessionFactory. We can refer to beans configured in our spring configuration by using annotation @Resource so we will use this annotation to get instance of bean identified as sessionFactory.in spring-config.xml

package com.techcielo.spring4.dao;

import javax.annotation.Resource;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class BaseDAO {
      
       @Resource(name="sessionFactory")
       protected SessionFactory sessionFactory;

       public void setSessionFactory(SessionFactory sessionFactory) {
              this.sessionFactory = sessionFactory;
       }
      
       protected Session getSession(){
              return sessionFactory.openSession();
       }     
}


Now we will create one entity class (to make it simple we will select an entity which has no dependency) for Table Products and we identify it as Product. Following is the table structure.



First we will take only two fields ProductId (as an identifier) and ProductName. Rest of the fields we will use in consecutive sections to establish different relationships in hibernate.       

package com.techcielo.spring4.bean;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="products")
public class Product {
            @Id
            @Column(name="ProductID")
            private int prodID;
           
            @Column(name="ProductName")
            private String name;
           
                       
            public int getProdID() {
                        return prodID;
            }

            public void setProdID(int prodID) {
                        this.prodID = prodID;
            }

            public String getName() {
                        return name;
            }

            public void setName(String name) {
                        this.name = name;
            }                      
}

Annotation @Entity tells Hibernate configuration that this is hibernate entity, @Table annotation tells which table this entity is attached to @Id tell which instance variable (in this case prodID) will be used as identifier for this class. @Column annotation tells to which column respective local variable will be attached to. Now we will add this class information in spring configuration file’s sessionFactory bean by changing it as follows.

After this step following will be our eclipse project structure.



<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      <property name="dataSource">
            <ref bean="dataSource" />
      </property>
      <property name="hibernateProperties">
            <props>
                  <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                  <prop key="hibernate.show_sql">true</prop>
            </props>
      </property>
      <property name="annotatedClasses">
            <list>
                  <value>com.techcielo.spring4.bean.Product</value>
            </list>
      </property>
</bean>


This change tells session factory that class Product is hibernate entity with annotations and need to be interpreted accordingly. Now time to change our DAO class and get the product from DB for which id is passed. So lets change getProduct method of this class as follows.

public Product getProduct(int id){
       System.out.println("In DAO class");
       try{
              return (Product) getSession().get(Product.class, id);
       }
       catch(HibernateException e){
              System.out.println("Error occured while getting data");
       }
       return null;
}

And in main class we get this instance and print value of this instance.

package com.techcielo.spring4.main;

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

import com.techcielo.spring4.bean.Product;
import com.techcielo.spring4.service.ProductService;

public class Main {
       public static void main(String[] args)
       {
              //Build application context by reading spring-config.xml
              ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"spring-config.xml"});
             
              //Get an instance of ProductService class;
              ProductService prdSvc = (ProductService) ctx.getBean("prodSvc");
             
              //Call getProduct method of ProductService
              Product prod = prdSvc.getProduct(1);
             
              System.out.println("Got the product:"+prod.getName());
             
       }
}

And when you run this class following will be output.


Jan 03, 2014 3:07:13 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@17e845a: startup date [Fri Jan 03 15:07:13 IST 2014]; root of context hierarchy
Jan 03, 2014 3:07:13 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring-config.xml]
Jan 03, 2014 3:07:14 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Jan 03, 2014 3:07:14 PM org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
Jan 03, 2014 3:07:14 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.7.Final}
Jan 03, 2014 3:07:14 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Jan 03, 2014 3:07:14 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
Jan 03, 2014 3:07:14 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
Jan 03, 2014 3:07:14 PM org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [3] less than 4
Jan 03, 2014 3:07:14 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
Jan 03, 2014 3:07:14 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
In Service class...will call DAO
In DAO class
Hibernate: select product0_.ProductID as ProductID3_0_, product0_.ProductName as ProductN2_3_0_ from products product0_ where product0_.ProductID=?
Got the product:Chai