Monday, March 31, 2014

Configuring Hibernate in Spring Framework.

tl;dr to configure Hibernate in Spring, you need to configure a bean that implements org.hibernate.SessionFactory. Configuring this bean involves specifying the data source, the Object-to-Database mapping (if you using xml for the mapping) and the hibernate properties.

Last post in the Configuring Spring series looked into how to configure data sources, a basic requirement, regardless of the actual method in which your application would communicate with the database server: either via plain JDBC or via an ORM tool.

This post would look into how to configure Spring to use Hibernate. Hibernate is an object-relational mapping library for the Java language, that provides a framework for mapping an object-oriented domain model to a traditional relational database.

In how to configure data sources in Spring, we saw how the data source is the entry point to having your application communicate with a database server; how it is configured and retrieved for use in your application by classes that are specifically created to handle interaction with the database (DAO classes)

In configuring hibernate to work with Spring Framework, the data source still remains the primary entry point, but it would not be what your application code (or DAO class if you go with this pattern) would have to use directly in other to communicate with the database. With Hibernate, your application code requires something called the SessionFactory

So the process of configuring Spring to work with Hibernate is basically then all about configuration the bean that would make the SessionFactory available for you.



So what is the SessionFactory? It is the factory that makes available an Hibernate Session. Let me explain:

When you want to work with Hibernate, you do so via Hibernate's main interface: org.hibernate.Session This interface provides the basic data access functionality: save, update, delete, read etc. Your applications DAO would perform its actions using classes that implements these interfaces.

But you don't get to retrieve Hibernate Session directly. The usually way is to get the Hibernate Session through an implementation of another interface: the org.hibernate.SessionFactory interface. Which has the sole task of creating and returning an Hibernate Session when needed and also takes care of opening, closing and managing of the Session returned.

So when configuring hibernate to work with Spring, you are interested in setting things up such that you have the SessionFactory available for use. Your configuration activities then revolves around providing the properties needed by the spring bean that implements the SessionFactory in other to make it ready to be used; that is, ready to make available Hibernate Session for you. And the properties solely needed includes:

1. Configuring the needed data source.
2. Providing the Object to Database Mapping via mappingResources.
3. Specifying the Hibernate Properties via hibernateProperties.

Before we go into how the configuration may look, let's briefly go over the 3 things mentioned above.

Configuring data sources is already covered in a previous post, so won't talk about that here.

What does it mean to provide the Object-to-Database mapping?

Hibernate is an Object Relational Mapping (ORM) tool. An ORM tool allows you to map Java (or any OOP) class into tables in a database. This allows a programmer to work with databases as if it was absolutely in the same OOP domain the programming language lives in.

In other to get this mapping going, since we are using Hibernate here, Hibernate requires you to provide a mapping file that stipulates your classes and which tables they need to be mapped to in your database. There are two ways you can provide this mapping. Historically via xml (which must have the extension hbm.xml) and more commonly now via annotations. A trivial example of what these kind of mapping looks like:

book.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping.dtd">
<hibernate-mapping>
 <class name="org.myspring.Book" table="BOOK">
     <id column="BOOK_ID" name="bookId" type="java.lang.Long">
   <generator class="org.hibernate.id.TableHiLoGenerator">
      <param name="table">idgen</param>
            <param name="column">NEXT</param>
    </generator>
     </id>
<property column="BOOK_TITLE" name="bookTitle" type="java.lang.String"/>
<property column="AUTHOR_NAME" name="authorName" type="java.lang.String"/>
<property column="PUBLISHED_DATE" length="4" name="publishedDate" type="java.util.Date"/>
 </class>
</hibernate-mapping>
The annotated class equivalent would look like:
Book.class

@Entity
class Book{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long bookId;

@Column(name="BOOK_TITLE")
private String bookTitle;

@Column(name="AUTHOR_NAME")
private String authorName;

@Column(name="PUBLISHED_DATE")
private Date publishedDate;

public Book() {}

}

The classes mapped this way via annotations are usually referred to as Entities. For a basic introduction to entity mapping read Understanding Entity Relationship Mapping, Part 1: The basic concepts and Understanding Entity Relationship Mapping, Part 2: Annotating Entity Relationships

So basically when you configure the Hibernate SessionFactory and you use XML for the ORM mapping, you would need to specify the location of the mapping file.

Hibernate properties on the other hand is used to specify Hibernate specific configuration like which SQL Dialect should be used etc.

Spring provides 2 classes that implements HibernateSession factory that you can wire up as a bean that creates Hibernate's Session for you. You use either of the two depending on how your Object to Database mapping is done. If you use XML mapping then use org.springframework.orm.hibernate3.LocalSessionFactoryBean, if mapping is done via annotations, then use org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean

With all the explanation out of the way, configuring hibernate for use with XML mapping would take this form:

---
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost/userapp"></property>
    <property name="username" value="sa"></property>
    <property name="password" value=""></property>
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
    <list>
         <value>book.hbm.xml </value>
     </list>
</property>
<property name="hibernateProperties">
    <props>
         <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
         <prop key="hibernate.show_sql">true</prop>
    </props>
</property>
</bean>
---

The mappingResources property is used to specify the hbm.xml file, while the hibernateProperties is used to list the Hibernate specific properties.

If you are already familiar with Hibernate you would be aware of another Hibernate specific configuration file that ends with "cfg.xml" extension. It is possible for you to move all the hibernate specific configuration to this file and then link it into your bean definition. Doing so would have you drop the hibernateProperties for the configLocation. Your bean configuration would then look thus:


<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingResources">
            <list>
               <value>book.hbm.xml </value>
            </list>
        </property>
     <property name="configLocation" value="classpath:book.cfg.xml"/>
</bean>

it is even possible to drop the mappingResources  and specify the location of the mapping file in the cfg.xml file. The dataSource information too can go into the cfg.xml file. What you place in where is totally up to you. Just be aware that you don't have to duplicate the information across the cfg.xml and in the bean configuration. The best practice usually is to put all the hibernate specific configuration in the cfg.xml file, while dataSource configuration stays in your Spring config file since other beans might need to reference it.

That is it for XML. Configuring hibernate to use annotations for mapping is not very much different.

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="org.mySpring"/>
<property name="hibernateProperties">
     <props>
           <prop key="dialect">org.hibernate.dialect.HSQLDialect</prop>
      </props>
</property>
</bean>

The thing to note is that when using annotation, AnnotationSessionFactoryBean  is wired up instead and you need to specify where to scan for annotated entities using the packagesToScan property.

With a SessionFactory bean wired thus, it is not possible to make use of it in your DAO class

@Component //meaning it would be auto scanned and wired
class BookDAO {

@Autowired
private SessionFactory sessionFactory;

public void save(Book book){
   sessionFactory.getCurrentSession().save(book);
}

}

2 comments:

Nandom said...

Hi, Its nice to meet with you after googling on a Solution on Spring Hibernate. I use EJB3 a lot and I would like to use Spring Hibernate for a particular project. The problem I am facing is the too much of code I am writing on Spring. For instance, I have to create the interface, then implement the interface in a class with the @Repository annotation, then I will have to create the service interface and then implement it in the class with the @Service annotation. Is there a way to write my Spring without those interfaces? i.e to only have one class that will autowire the SessionFactory and then call the class to persist my data to the data base? pls help me, I really need your help

Dadepo Aderemi said...

@Nandom
Any reason why you using Hibernate and Spring this way instead of just using JPA as implemented by Spring?

That way you can have spring configure and provide you with the entity manager which you can then use to persist data....

Also have you checked out Spring Data JPA? http://projects.spring.io/spring-data-jpa/