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.
<?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.
<?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.
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.
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!
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:
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
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