Spring MVC and Hibernate Transaction Tutorial

1- Introduction

The document is based on:
  • Spring 4 MVC  (XML Config)

  • Spring AOP (Config)

  • Hibernate 4

In this document I will guide you to create a project using:
  • Spring MVC
  • Hibernate
  • Spring Transaction sử dụng AOP
More advanced, we will configure to use Transaction on VIEW.
Using  "Spring Hibernate Transaction" means that you grant the open/close authority of a transaction for Spring, and you don't need to care about handling it.

This is a typical code part. When you use Hibernate, you have to start 1 transaction and close it after you finish it..
Session session = sessionFactory.getCurrentSession();

try {
  session.getTransaction().begin();
 
  // Todo something here
 
  session.getTransaction().commit();
} catch (Exception e) {
  e.printStackTrace();
  session.getTransaction().rollback();
}
The above manipulation is manual, and it has difficulties in handling professional situations on various methods. Suppose A person send money to B person. You have write two methods. The first method is to debit the A person's account. The second method is to credit the B person's account. These two methods must belong to a Transaction.

Spring AOP allows the configuration to help you manage the Transaction automatically.
AOP is an aspect-oriented programming. You may not need to understand it, but you learn how to configure  Spring AOP for your purpose. However, if you really want to know what AOP can do, you can see the AOP document at:

2- SQL Script

ORACLE:
create table DEPARTMENT (
      DEPT_ID number(10,0) not null,
      DEPT_NAME varchar2(255 char) not null,
      DEPT_NO varchar2(20 char) not null unique,
      LOCATION varchar2(255 char),
      primary key (DEPT_ID)
  );
MySQL:
create table DEPARTMENT (
  DEPT_ID integer not null,
  DEPT_NAME varchar(255) not null,
  DEPT_NO varchar(20) not null,
  LOCATION varchar(255),
  primary key (DEPT_ID),
  unique (DEPT_NO)
);
SQLServer:
Create table DEPARTMENT (
   DEPT_ID int not null,
   DEPT_NAME varchar(255) not null,
   DEPT_NO varchar(20) not null,
   LOCATION varchar(255),
   primary key (DEPT_ID),
   unique (DEPT_NO)
);

3- Create project

  • File/New/Other..
Enter:
  • Group ID: org.o7planning
  • Artifact ID: SpringMVCHibernateTransaction
  • Package: org.o7planning.tutorial.springhibernate
Project was created.
Do not worry with the error message when Project has been created. The reason is that you have not declared Servlet library.
Note:
Eclipse 4.4 (Luna) create Maven project structure may be wrong. You need to check.

4- Configuring Maven

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
   http://maven.apache.org/maven-v4_0_0.xsd">
   
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.o7planning</groupId>
   <artifactId>SpringMVCHibernateTransaction</artifactId>
   <packaging>war</packaging>
   <version>1.0.0</version>
   <name>SpringMVCHibernateTransaction Maven Webapp</name>
   <url>http://maven.apache.org</url>


   <properties>
       <java-version>1.7</java-version>
   </properties>

   <repositories>
       <!-- Repository for ORACLE JDBC Driver -->
       <repository>
           <id>codelds</id>
           <url>https://code.lds.org/nexus/content/groups/main-repo</url>
       </repository>
   </repositories>

   <dependencies>

       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>3.8.1</version>
           <scope>test</scope>
       </dependency>

       <!-- Servlet API -->
       <!-- http://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <version>3.1.0</version>
           <scope>provided</scope>
       </dependency>

       <!-- Jstl for jsp page -->
       <!-- http://mvnrepository.com/artifact/javax.servlet/jstl -->
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>jstl</artifactId>
           <version>1.2</version>
       </dependency>


       <!-- JSP API -->
       <!-- http://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
       <dependency>
           <groupId>javax.servlet.jsp</groupId>
           <artifactId>jsp-api</artifactId>
           <version>2.2</version>
           <scope>provided</scope>
       </dependency>

       <!-- Spring dependencies -->
       <!-- http://mvnrepository.com/artifact/org.springframework/spring-core -->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-core</artifactId>
           <version>4.1.4.RELEASE</version>
       </dependency>

       <!-- http://mvnrepository.com/artifact/org.springframework/spring-web -->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-web</artifactId>
           <version>4.1.4.RELEASE</version>
       </dependency>

       <!-- http://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-webmvc</artifactId>
           <version>4.1.4.RELEASE</version>
       </dependency>

       <!-- http://mvnrepository.com/artifact/org.springframework/spring-orm -->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-orm</artifactId>
           <version>4.1.4.RELEASE</version>
       </dependency>

       <!-- Hibernate -->
       <!-- http://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
       <dependency>
           <groupId>org.hibernate</groupId>
           <artifactId>hibernate-core</artifactId>
           <version>4.3.8.Final</version>
       </dependency>

       <!-- http://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
       <dependency>
           <groupId>org.hibernate</groupId>
           <artifactId>hibernate-entitymanager</artifactId>
           <version>4.3.8.Final</version>
       </dependency>


       <!-- http://mvnrepository.com/artifact/org.hibernate/hibernate-c3p0 -->
       <dependency>
           <groupId>org.hibernate</groupId>
           <artifactId>hibernate-c3p0</artifactId>
           <version>4.3.8.Final</version>
       </dependency>


       <!-- MySQL JDBC driver -->
       <!-- http://mvnrepository.com/artifact/mysql/mysql-connector-java -->
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>5.1.34</version>
       </dependency>

       <!-- Oracle JDBC driver -->
       <dependency>
           <groupId>com.oracle</groupId>
           <artifactId>ojdbc6</artifactId>
           <version>11.2.0.3</version>
       </dependency>

       <!-- SQLServer JDBC driver (JTDS) -->
       <!-- http://mvnrepository.com/artifact/net.sourceforge.jtds/jtds -->
       <dependency>
           <groupId>net.sourceforge.jtds</groupId>
           <artifactId>jtds</artifactId>
           <version>1.3.1</version>
       </dependency>


   </dependencies>


   <build>
       <finalName>SpringMVCHibernateTransaction</finalName>
       <plugins>
       
           <!-- Config: Maven Tomcat Plugin -->
           <!-- http://mvnrepository.com/artifact/org.apache.tomcat.maven/tomcat7-maven-plugin -->
           <plugin>
               <groupId>org.apache.tomcat.maven</groupId>
               <artifactId>tomcat7-maven-plugin</artifactId>
               <version>2.2</version>
               <!-- Config: contextPath and Port (Default - /SpringMVCHibernateTransaction : 8080) -->
               <!--
               <configuration>
                   <path>/</path>
                   <port>8899</port>
               </configuration>
               -->    
           </plugin>
       </plugins>
   </build>    
   

</project>

5- Configuring Spring

Configure web.xml.

SpringContextListener will read the configuration file by parameter contextConfigLocation:
WEB-INF/web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   version="3.0">

   <display-name>Archetype Created Web Application</display-name>
   <servlet>
       <servlet-name>spring-mvc</servlet-name>
       <servlet-class>
           org.springframework.web.servlet.DispatcherServlet
       </servlet-class>
       <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
       <servlet-name>spring-mvc</servlet-name>
       <url-pattern>/</url-pattern>
   </servlet-mapping>

   <!-- Other XML Configuration -->
   <!-- Load by Spring ContextLoaderListener -->
   <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>
          /WEB-INF/data-source-hiber-cfg.xml,
          /WEB-INF/dao-cfg.xml
          /WEB-INF/transaction-cfg.xml
        </param-value>        
   </context-param>


   <!-- Spring ContextLoaderListener -->
   <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>

</web-app>
Configure Spring MVC:
WEB-INF/spring-mvc-servlet.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: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-4.1.xsd  
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.1.xsd  
     http://www.springframework.org/schema/mvc
     http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

   <context:component-scan base-package="org.o7planning.tutorial.springhibernate.*" />

   <context:annotation-config />

   <bean
       class="org.springframework.web.servlet.view.InternalResourceViewResolver">

       <property name="prefix">
           <value>/WEB-INF/pages/</value>
       </property>

       <property name="suffix">
           <value>.jsp</value>
       </property>

   </bean>

</beans>
The Datasource and Hibernate configuration in file:
  • WEB-INF/data-source-hiber-cfg.xml
Here I use Oracle, but if you another database different from MySQL or SQLServer, you can see the below Appendix.

Here I configure Hibernate by the way of Spring. However, you can configure Hibernate in  hibernate.cfg.xml file by the conventional way of Hibernate, then you notify the position to Spring (See the below Appendix).

NOTE: Because you want Spring to manage Transaction, you should not configure in the Hibernate configuration.

  • <property name="current_session_context_class">thread</property>
WEB-INF/data-source-hiber-cfg.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.xsd">


  <bean id="myDataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
      <property name="url" value="jdbc:oracle:thin:@localhost:1521:db11g" />
      <property name="username" value="simplehr" />
      <property name="password" value="12345" />
  </bean>

  <bean id="mySessionFactory"
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      <property name="dataSource" ref="myDataSource" />

      <property name="annotatedClasses">
          <list>
              <value>org.o7planning.entity.Department</value>
          </list>
      </property>

      <property name="hibernateProperties">
          <props>
              <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
              <prop key="hibernate.show_sql">true</prop>
          </props>
      </property>
  </bean>
 
</beans>
Configure Transaction Manager (Spring AOP)
WEB-INF/transaction-cfg.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:tx="http://www.springframework.org/schema/tx"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.1.xsd        
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">

   <import resource="data-source-hiber-cfg.xml"/>

   <tx:annotation-driven proxy-target-class="true" transaction-manager="txManager"/>

   <bean id="txManager"
       class="org.springframework.orm.hibernate4.HibernateTransactionManager">
       <property name="sessionFactory" ref="mySessionFactory"/>
   </bean>

</beans>
Configure other beans:
WEB-INF/dao-cfg.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-4.1.xsd">



 <bean id="departmentDAO" class="org.o7planning.dao.impl.DepartmentDAOImpl">
     <property name="sessionFactory" ref="mySessionFactory" />
 </bean>


</beans>

6- Java Classes

Class Entity: Department
Department.java
package org.o7planning.entity;

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

@Entity
@Table(name = "DEPARTMENT", uniqueConstraints = { @UniqueConstraint(columnNames = { "DEPT_NO" }) })
public class Department {

   private Integer deptId;
   private String deptNo;

   private String deptName;
   private String location;

   public Department() {
   }

   public Department(Integer deptId, String deptName, String location) {
       this.deptId = deptId;
       this.deptNo = "D" + this.deptId;
       this.deptName = deptName;
       this.location = location;
   }

   @Id
   @Column(name = "DEPT_ID")
   public Integer getDeptId() {
       return deptId;
   }

   public void setDeptId(Integer deptId) {
       this.deptId = deptId;
   }

   @Column(name = "DEPT_NO", length = 20, nullable = false)
   public String getDeptNo() {
       return deptNo;
   }

   public void setDeptNo(String deptNo) {
       this.deptNo = deptNo;
   }

   @Column(name = "DEPT_NAME", nullable = false)
   public String getDeptName() {
       return deptName;
   }

   public void setDeptName(String deptName) {
       this.deptName = deptName;
   }

   @Column(name = "LOCATION")
   public String getLocation() {
       return location;
   }

   public void setLocation(String location) {
       this.location = location;
   }

}
 
DepartmentDAO.java
package org.o7planning.dao;

import java.util.List;

import org.o7planning.entity.Department;

public interface DepartmentDAO {

  public List<Department> listDepartment() ;
 
  public Integer getMaxDeptId();
 
  public void createDepartment(String name,String location);  
}
DepartmentDAOImpl.java
package org.o7planning.dao.impl;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.dao.DepartmentDAO;
import org.o7planning.entity.Department;
import org.springframework.transaction.annotation.Transactional;

@Transactional
public class DepartmentDAOImpl implements DepartmentDAO {

  private SessionFactory sessionFactory;

  public void setSessionFactory(SessionFactory sessionFactory) {
      this.sessionFactory = sessionFactory;
  }

  @SuppressWarnings("unchecked")
  public List<Department> listDepartment() {
      Session session = this.sessionFactory.getCurrentSession();

      List<Department> list = session.createQuery("from Department").list();

      return list;
  }

  public Integer getMaxDeptId() {
      Session session = this.sessionFactory.getCurrentSession();
      String sql = "Select max(d.deptId) from Department d ";
      Query query = session.createQuery(sql);
      Integer maxDeptId = (Integer) query.uniqueResult();
      if (maxDeptId == null) {
          return 0;
      }
      return maxDeptId;
  }

  public void createDepartment(String name, String location) {
      Integer deptId = getMaxDeptId() + 1;
      Department dept = new Department();
      dept.setDeptId(deptId);
      dept.setDeptNo("D" + deptId);
      dept.setDeptName(name);
      dept.setLocation(location);
      Session session = this.sessionFactory.getCurrentSession();
      session.persist(dept);
  }

}
You can see a notice on Eclipse that methods are influenced by AOP (As the below illustration).
As you know, you can do the configuration in xml file in order to inform Spring that some class is a Bean. Another way is that other classes enclosed with a  @Controller or  @Service annotation in the scanning scope of Spring are also considered as Beans by Spring.

  @Autowired annotation is attached to fields belonging to Controller class or Service class for ordering Spring automatically to have a dependency injection.
<!-- See the configuration code scanning range of Spring -->
<!-- spring-mvc-servlet.xml -->

<context:component-scan base-package="org.o7planning.tutorial.springhibernate.*" />
The class annotated by @Controller or @Service be regarded as the Bean, and Spring will automatically inject dependencies in the Field annotated by @Autowired annotation.
MainController.java
package org.o7planning.tutorial.springhibernate.controller;

import java.util.List;

import org.o7planning.dao.DepartmentDAO;
import org.o7planning.entity.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController {

 @Autowired
 private DepartmentDAO departmentDAO;

 @RequestMapping({ "/", "/home", "/index" })
 public String home(Model model) {
     return "index";
 }

 @RequestMapping({ "/deptList" })
 public String deptList(Model model) {
     departmentDAO.createDepartment("Dept Name", "Dept Location");

     List<Department> list = departmentDAO.listDepartment();
     for (Department dept : list) {
         System.out.println("Dept No " + dept.getDeptNo());
     }
     model.addAttribute("departments", list);
     return "deptList";
 }
}

7- Views

index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
  <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <title>Home</title>
  </head>
  <body>
      <div align="center">
          <h1>Home Page</h1>
           <a href="deptList">Department List</a>
      </div>
  </body>
</html>
deptList.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
 <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <title>Department List</title>
 </head>
 <body>
     <div align="center">
         <h1>Department List</h1>
         <table border="1">
             <th>No</th>
             <th>Dept No</th>
             <th>Dept Name</th>
             <th>Location</th>
           
             <c:forEach var="dept" items="${departments}" varStatus="status">
             <tr>
                 <td>${status.index + 1}</td>
                 <td>${dept.deptNo}</td>
                 <td>${dept.deptName}</td>
                 <td>${dept.location}</td>                            
             </tr>
             </c:forEach>                
         </table>
     </div>
 </body>
</html>

8- Run Application

In the first, before running the application you need to build the entire project.
Right-click the project and select:

Run Configurations:

Enter:
  • Name: Run SpringMVCHibernateTransaction
  • Base directory: ${workspace_loc:/SpringMVCHibernateTransaction}
  • Goals: tomcat7:run
Click Run:

9- Appendix

Datasource & Hibernate configuration for MySQL:
WEB-INF/data-source-hiber-cfg.xml (MySQL)
<?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.xsd">


  <bean id="myDataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver" />
      <property name="url" value="jdbc:mysql://localhost:3306/simplehr" />
      <property name="username" value="root" />
      <property name="password" value="12345" />
  </bean>

  <bean id="mySessionFactory"
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      <property name="dataSource" ref="myDataSource" />

      <property name="annotatedClasses">
          <list>
              <value>org.o7planning.entity.Department</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>
 
</beans>
WEB-INF/data-source-hiber-cfg.xml (SQLServer)
<?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.xsd">


  <bean id="myDataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver" />
      <property name="url" value="jdbc:jtds:sqlserver://localhost:1433/simplehr;instance=SQLEXPRESS" />
      <property name="username" value="sa" />
      <property name="password" value="12345" />
  </bean>

  <bean id="mySessionFactory"
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      <property name="dataSource" ref="myDataSource" />

      <property name="annotatedClasses">
          <list>
              <value>org.o7planning.entity.Department</value>
          </list>
      </property>

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