Persistence with Hibernate & Spring

Now that we have seen how Spring MVC 3 works, and have created a basic domain object to contain our data, let’s take it a step further and persist the greetings that are entered into the database that we just got up and running. We are getting closer and closer to a Twitter scenario where users can display short text messages or “tweets.” We just need to integrate our persistence framework, Hibernate.

As described on its wiki page, Hibernate is an object-relational mapping (ORM) library for the Java language, providing a framework for mapping an object-oriented domain model to a traditional relational database. Hibernate solves object-relational impedance mismatch problems by replacing direct persistence-related database accesses with high-level object handling functions.

We will use Hibernate to persist our greeting data in the database now. We will add a Data Access Object (DAO) layer to our application, and then add a Service layer, and see how each layer has its own “role” and why that is so important. Transactions will be configured as well. The Spring container will help us locate our resources and wire them together.

That’s right, The Spring Container. Somehow we managed to get this far without talking about the core concept of Spring, which is the container! Spring MVC is just one feature of Spring. If you are new to Spring, then the wiki has a great overview. In more words or less, the Spring Container provides a way to configure and lookup objects easily, and manage their lifecycle. Applications can grow into a complex web of dependencies and can be difficult to test and maintain if the dependencies are not managed correctly.

Spring helps with this problem. It enables us to wire dependencies together with dependency injection. At this stage in the application, it might not be apparent why dependency injection is so important so let’s have a look at that later. For now, just envision your web application like a machine composed of several parts. Each machine part must have a name, a specific role, an easy way to locate the part, and must be easily replaceable. Most important is that each part must perform its own tasks, and not the tasks of the other parts.

1. Add these dependencies to your pom.xml

You might wonder why we need so many dependencies here but just add them ;-)

<dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-orm</artifactId>
    	<version>3.0.5.RELEASE</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-tx</artifactId>
    	<version>3.0.5.RELEASE</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>   
<dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-core</artifactId>
    	<version>3.0.5.RELEASE</version>
    	<type>jar</type>
    	<scope>compile</scope>
    	<exclusions>
    		<exclusion>
    			<artifactId>commons-logging</artifactId>
    			<groupId>commons-logging</groupId>
    		</exclusion>
    	</exclusions>
</dependency>
<dependency>
    	<groupId>commons-digester</groupId>
    	<artifactId>commons-digester</artifactId>
    	<version>2.1</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>commons-collections</groupId>
    	<artifactId>commons-collections</artifactId>
    	<version>3.2.1</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>org.hibernate</groupId>
    	<artifactId>hibernate-core</artifactId>
    	<version>3.3.2.GA</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>javax.persistence</groupId>
    	<artifactId>persistence-api</artifactId>
    	<version>1.0</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency> 
<dependency>
	 <groupId>commons-dbcp</groupId>
	 <artifactId>commons-dbcp</artifactId>
	 <version>1.4</version>
</dependency>    
<dependency>
    	<groupId>org.slf4j</groupId>
    	<artifactId>slf4j-api</artifactId>
    	<version>1.6.1</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>org.slf4j</groupId>
    	<artifactId>slf4j-log4j12</artifactId>
    	<version>1.6.1</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>cglib</groupId>
    	<artifactId>cglib-nodep</artifactId>
    	<version>2.2</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>org.hibernate</groupId>
    	<artifactId>hibernate-annotations</artifactId>
    	<version>3.4.0.GA</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>
<dependency>
    	<groupId>jboss</groupId>
    	<artifactId>javassist</artifactId>
    	<version>3.7.ga</version>
    	<type>jar</type>
    	<scope>compile</scope>
</dependency>

2. Add a datasource configuration file to WEB-INF called datasource-context.xml

The wonderful thing about using hibernate and spring is that they integrate together perfectly. The first step to integrating hibernate to a spring web application is to create a datasource context file. Here we will define our Session Factory, our database drivers, and we will set up a hibernate transaction manager. We will also specify that we will use configure our transactions with annotations instead of the “old school” xml way.

If you would like to switch to a different database like mysql later, here is where you would change the configuration for a different session factory and driver.

WEB-INF/datasource-context.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"
       	xmlns:p="http://www.springframework.org/schema/p" 
       	xmlns:tx="http://www.springframework.org/schema/tx"
       	xmlns:context="http://www.springframework.org/schema/context"
       	xsi:schemaLocation="
			http://www.springframework.org/schema/beans 
			http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
			http://www.springframework.org/schema/tx 
			http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
			http://www.springframework.org/schema/context
			http://www.springframework.org/schema/context/spring-context-3.0.xsd
	   		">
 
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="packagesToScan" value="com.bitbybit" />
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect"> org.hibernate.dialect.HSQLDialect</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">create</prop>
			</props>
		</property>
	</bean>				 
 
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
		<property name="url" value="jdbc:hsqldb:hsql://localhost"/>
		<property name="username" value="sa"/>
		<property name="password" value=""/>
	</bean>		
 
	<tx:annotation-driven transaction-manager="transactionManager" />		
 
        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
               <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
 
</beans>

3. Import the datasource configuration from applicationContext.xml

To make sure that our spring application is aware of our new datasource configuration, we must import it now.

WEB-INF/applicationContext.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" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	   		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	   		http://www.springframework.org/schema/context
	   		http://www.springframework.org/schema/context/spring-context-3.0.xsd
			http://www.springframework.org/schema/mvc 
			http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
 
	<context:annotation-config />
 
	<context:component-scan base-package="com.bitbybit" />
 
	<mvc:annotation-driven /> 
 
	<import resource="datasource-context.xml" />
 
</beans>

4. Update Greeting.java to include ID, hibernate annotation, and date field

Let’s revisit our domain object Greeting.java and make it persistable. We have specified above that we would like to use annotations for hibernate configuration, so all need to do is add annotations in Greeting.java to link it to the table GREETING. Above the class declaration, we write @Entity and then @Table(name = “GREETING”). These are Hibernate annotations, not Spring annotations!

You are probably wondering why we aren’t creating the table GREETING by hand in our new database using SQL code. Hibernate will create the table for us automatically every time we redeploy and relaunch the application, so we can sit back and be lazy! Above, we configured this:
<prop key=”hibernate.hbm2ddl.auto”>create</prop>
but that can easily be reconfigured so tables won’t be recreated automatically every time we redeploy (don’t do this in a production environment, EVER!)

We need to make the Greeting object serializable, and give it a serialVersionUID. Use Eclipse to generate the serialVersionUID. We will also add annotations to the fields that we would like to persist, including a new date field. This will make it possible for us to see when greetings were created.

Greeting.java
package com.bitbybit.domain;
 
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
 
@Entity
@Table(name = "GREETING")
public class Greeting implements Serializable {
 
	private static final long serialVersionUID = 7498045397774366499L;
 
	@Id
	@Column(name = "ID")
	@GeneratedValue
	private Integer id;
 
	@Column(name = "GREETING_TEXT")
	private String greetingText;
 
	@Temporal(TemporalType.TIMESTAMP)
        @Column(name = "GREETING_DATE")
	private Date greetingDate;
 
	public Integer getId() {
		return id;
	}
 
	public void setId(Integer id) {
		this.id = id;
	}
 
	public String getGreetingText() {
		return greetingText;
	}
 
	public void setGreetingText(String greetingText) {
		this.greetingText = greetingText;
	}
 
	public Date getGreetingDate() {
		return greetingDate;
	}
 
	public void setGreetingDate(Date greetingDate) {
		this.greetingDate = greetingDate;
	} 
 
}

5. Add a DAO class called GreetingDao.java

Time to add a new layer now. This is the Data Access Layer (DAO). The architectural goal of the Data Access Layer is to encapsulate all the logic of accessing application data in one layer. This could be considered one of the lowest levels in the application chain. The DAO is responsible just for getting and saving data, and not processing the data in any way. It is very important that we keep all the data access code in one layer (i.e. the DAO classes), and not have data access code scattered around the application in the web classes etc, since this makes the application very inflexible. If we would like to later switch frameworks from hibernate to ibatis for example, all we need to do is change the DAO implementation classes if we have implemented them well.

The Data Access Object Layer is often called the “Database Access Object Layer” but this is not correct. It is a layer for accessing Data, and the data could come from anywhere – a file system, database, etc.

GreetingDao.java
package com.bitbybit.dao;
 
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Repository;
import com.bitbybit.domain.Greeting;
import org.springframework.beans.factory.annotation.Autowired;
 
@Repository
public class GreetingDao {
 
	protected static Logger logger = Logger.getLogger("GreetingDao");
 
	@Autowired
	private SessionFactory sessionFactory;
 
	public List<Greeting> getAllGreetings() {
		Session session = sessionFactory.getCurrentSession();		
		Query q = session.createQuery("select g from Greeting g order by id desc");
		List<Greeting> greetingList = q.list(); 
	        return greetingList;			
	}
 
	public void addGreeting(Greeting greeting) {
		Session session = sessionFactory.getCurrentSession();	
		session.save(greeting);
	}
 
}

6. Add a Service class called GreetingService.java

Now we will add the Service layer, which lies just one layer above the DAO layer. In the implementation example below of GreetingService.java, it is not obvious what the point of this class is, since all it does is call methods in the DAO class. It is common practice to have an architecture with a Service class and a DAO class, instead of just having one “all purpose” class although this is still debated and misunderstood.

Why have a Service layer? Suppose we would like to add some logic when we retrieve the greeting. We don’t want to have too much logic in the web layer or in the persistence layer, so we will put it in the Service layer. We might want to check the greeting size, or check if the greeting contains inappropriate language and this is where we would do it. We also need a place for transactions.

Why have transactions in the Service layer and not the DAO layer? The Service layer might have methods with multiple lookups and insertions on the database. We might interact with other DAOs like the UserDao for example. If anything were to go wrong inside a method call, it would be better to rollback the entire method rather than just do half a unit of work, and leave the database in an inconsistent state, right? That is why transactions should go in the Service layer. Even if we don’t need the Service layer right now, in most cases we will need it as the application grows more complex, so that is why I have added GreetingService.java to this application. Just how do we make this class transactional? Don’t forget, we have already set up the HibernateTransactionManager and specified we’re using transaction annotations, in the datasource-context.xml file. So now all we need to do is add the @Transactional annotation above the class declaration and now all methods will occur within a transaction!

GreetingService.java
package com.bitbybit.service;
 
import java.util.List;
import org.apache.log4j.Logger;
import com.bitbybit.dao.GreetingDao;
import com.bitbybit.domain.Greeting;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
 
@Service
@Transactional
public class GreetingService {
 
	protected static Logger logger = Logger.getLogger("GreetingService");
 
	@Autowired
	private GreetingDao greetingDao;
 
	public List<Greeting> getAllGreetings() {		
		return greetingDao.getAllGreetings();		
	}
 
	public void addGreeting(Greeting greeting) {		
		greetingDao.addGreeting(greeting);
	}
 
}

7. Using Spring annotations for classpath scanning and wiring dependencies…

Take a look at the other annotations that we used here. GreetingService has a @Service annotation above its class declaration, and GreetingDao has @Repository annotation above its class declaration. What is that all about? A while back we set up the configuration file to scan for components, with a package base path. Now we would like to register these classes in the spring container, naming and binding them to the JNDI context.

Here is the “old school” way of doing this:

<bean id="greetingDao" class="com.bitbybit.dao.GreetingDao">
<bean id="greetingService" class="com.bitbybit.service.GreetingService">

With annotations, we can just include the @Component annotation above class declarations. @Component serves as a generic stereotype for any Spring-managed component, while @Repository, @Service, and @Controller serve as specializations of @Component for more specific use cases (in the persistence, service, and presentation layers, respectively) and are more suited for special processing by tools or Spring aspects. Adding these annotations means that when the container does a component scan, these classes are picked up and registered.

The next step is wiring the components together (dependency injection). We will be using @Autowired to do this, but it is also possible with annotations that are not spring specific, @Inject and @Resource. There are 3 ways of injecting dependencies.

1. The simple field way

//GreetingService gets its greetingDao dependency like this
@Autowired
private GreetingDao greetingDao;

2. The constructor way

//GreetingController (in the example below) gets its greetingService dependency like this
private GreetingService greetingService;
@Autowired 
public GreetingController(GreetingService greetingService) {
	this.greetingService = greetingService;
}

3. The setter way

//GreetingController could also get its greetingService dependency like this
private GreetingService greetingService;
@Autowired 
public void setGreetingService(GreetingService greetingService) {
        this.greetingService = greetingService;
}

Now let’s modify the GreetingController class to get its GreetingService dependency through its constructor:

GreetingController.java
package com.bitbybit.web.controller;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Date;
import org.apache.log4j.Logger;
import com.bitbybit.domain.Greeting;
import com.bitbybit.domain.Color;
import com.bitbybit.service.GreetingService;
import com.bitbybit.web.form.GreetingForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
@RequestMapping("/home")
public class GreetingController {
 
	protected static Logger logger = Logger.getLogger("GreetingController");
 
	private GreetingService greetingService;
 
	@Autowired 
	public GreetingController(GreetingService greetingService) {
		this.greetingService = greetingService;
	}
 
        //note there is no actual greetings.html file!! 
	@RequestMapping(value = "/addgreeting.html", method = RequestMethod.GET)
        public String showAddGreetingPage(@ModelAttribute("greetingform") GreetingForm greetingForm) {		
 
		logger.info("entering showAddGreetingPage()");
 
		//no need to add colorlist to the model anymore since it is defined at method level below
		//no need to have the model object in this method either
 
    	        //resolves to /WEB-INF/jsp/addgreeting.jsp
    	        return "addgreeting";
	}	
 
	@ModelAttribute("colorlist")
	public List<Color> populateColorList() {
		//color list is hardcoded for now...
		List<Color> colorList = new ArrayList<Color>();
		colorList.add(new Color("Indian Red", "F75D59"));
		colorList.add(new Color("Red", "FF0000"));
		colorList.add(new Color("Salmon", "F9966B"));
		colorList.add(new Color("Lemon Chiffon", "FFF8C6"));
		colorList.add(new Color("Olive Green", "BCE954"));
		colorList.add(new Color("Steel Blue", "C6DEFF"));
		colorList.add(new Color("Medium Purple", "9E7BFF"));
		return colorList;
	}	
 
	@RequestMapping(value = "/greetings.html", method = RequestMethod.POST)
	public String addGreetingAndShowAll(@ModelAttribute("greetingform") GreetingForm greetingForm,
			Map<String, Object> model) {		
 
		logger.info("entering addGreetingAndShowAll()");
 
		Greeting greeting = greetingForm.getGreeting();
                greeting.setGreetingDate(new Date());
		greetingService.addGreeting(greeting);  
 
		List<Greeting> greetings = greetingService.getAllGreetings();
		model.put("greetinglist", greetings);
 
	    	String selectedColorCode=greetingForm.getColor().getColorCode(); 
	    	if(selectedColorCode.equals("")) { //if no color selected, then make default white
	    		selectedColorCode="FFFFFF";
                }
	    	model.put("colorcode", selectedColorCode);
 
	    	// This will resolve to /WEB-INF/jsp/greetings.jsp
	    	return "greetings";
	} 
 
	//define the same url with GET so users can skip to the greetings page
	@RequestMapping(value = "/greetings.html", method = RequestMethod.GET)
	public String showAllGreetings(Map<String, Object> model) {		
 
		logger.info("entering showAllGreetings");
 
		List<Greeting> greetings = greetingService.getAllGreetings();
		model.put("greetinglist", greetings);		
		model.put("colorcode", "FFFFFF");
 
	    	// This will resolve to /WEB-INF/jsp/greetings.jsp
	    	return "greetings";
	}
 
}

8. Modify greetings.jsp to print the greeting list

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
 
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  	<title>Spring Greetings</title>
</head>
 
<body bgcolor='#<c:out value="${colorcode}" />'>
 
<h1>Spring Greetings</h1>
 
<c:if test="${not empty greetinglist}" >
	<c:forEach items="${greetinglist}" var="greeting">		
			<br/><b>@Anonymous</b> says<br/>	
			on <c:out value="${greeting.greetingDate}" /><br/>	
			<c:out value="${greeting.greetingText}" /><br/>			
	</c:forEach>	
</c:if>
<c:if test="${empty greetinglist}" >
	There are no greetings yet. 
</c:if>
 
<p><a href="/springgreetings/home/addgreeting.html">Add greeting</a><br/>
<a href="/springgreetings/">Home</a>
 
</body>
</html>

9. Redeploy the application to tomcat

$ mvn tomcat:redeploy
NOTE: If you ever find your deployment hanging or get this error: “java.lang.OutOfMemoryError: PermGen space” then you need to increase your memory in the tomcat configuration file called catalina.sh. Find this file inside tomcat/bin/catalina.sh and add the following to the beginning of the file (after the comments):

JAVA_OPTS=”-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m
-Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m
-XX:MaxPermSize=256m -XX:+DisableExplicitGC”

10. start the HSQLDB

If it is not installed then click here.

11. Run the application

http://localhost:8080/springgreetings/home/addgreeting.html

4 Comments
Kaushik wrote on 2013-04-18 at 05:32:50:
great article.. thanks!


Polina wrote on 2013-04-17 at 12:09:17:
Hello, your example is great. This is the best example which I find in internet space. I have one problem, because eclipse don't recognize jars download by maven. But this link may be helpful for those with same problem http://www.mkyong.com/maven/how-to-configure-m2_repo-variable-in-eclipse-ide/ . Thank you for this example :)


Anonym wrote on 2013-04-15 at 11:17:51:
How can I fix this error? Something is wrong with Hibernate? "The server encountered an internal error () that prevented it from fulfilling this request." Exception: "org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is org.hibernate.exception.GenericJDBCException: Cannot open connection org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:656) org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560) javax.servlet.http.HttpServlet.service(HttpServlet.java:641) javax.servlet.http.HttpServlet.service(HttpServlet.java:722) "


Anonymous wrote on 2013-04-15 at 10:27:44:
Error 500. The server encountered an internal error () that prevented it from fulfilling this request. exception: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is org.hibernate.exception.GenericJDBCException: Cannot open connection org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:656) org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560) javax.servlet.http.HttpServlet.service(HttpServlet.java:641) javax.servlet.http.HttpServlet.service(HttpServlet.java:722) Something is wrong.


Post a Comment

(required):