RSS

Monthly Archives: July 2014

Spring MVC with Hibernate


These days spring is most popular framework in the industry because it has lots of capabilities. Most probably large scale projects are using Spring as DI(Dependency Injection) framework with supporting of AOP(Aspect oriented programming) and Hibernate as ORM(Object relational mapping) framework in their backend. There is another cool feature came along with spring which is provide the MVC(Model View Control) architectural pattern.

In this post I’m going to focus on the spring MVC, DI, AOP. I’m not going to explain the hibernate mappings because it is in another context.

Lets look at the spring MVC request handling architecture.
Spring MVC

In the above diagram you can see there is controller class which is responsible for the request mediation. As a good practice we are not writing any business logic on this controller. Spring MVC is front end architecture and we have to have the layered architecture to separate the logic by concern. So thats why we use the backend service which is provide the business logic.

Lets go for the implementation of simple sample project with SpringMVC and Hibernate. I’m using maven project with the eclipse. You can download the full source of the project from here
Screen Shot 2014-07-26 at 1.03.03 PM

I have created layers by separating the concerns.
1. Controller layer (com.app.spring.contoller)
2. Service layer (com.app.spring.service)
3. Data Access layer (com.app.spring.dao)
4. Persistance layer (com.app.spring.model)

First look at the Model class Customer. This class is the one going to map with the DB table.

package com.app.spring.model;

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

/**
 * 
 * @author malalanayake
 *
 */
@Entity
@Table(name = "CUSTOMER")
public class Customer {

	@Id
	@Column(name = "id")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	private String name;
	private String address;

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "id=" + id + ", name=" + name + ", address=" + address;
	}
}

Data Access Object class – CustomerDAOImpl.java
In each layer we need to have interfaces which is provided the functionality and the concert implementation classes. So we have CustomerDAO Interface and CustomerDAOImpl class as follows.

package com.app.spring.dao;

import java.util.List;

import com.app.spring.model.Customer;

/**
 * 
 * @author malalanayake
 *
 */
public interface CustomerDAO {

	public void addCustomer(Customer p);

	public void updateCustomer(Customer p);

	public List<Customer> listCustomers();

	public Customer getCustomerById(int id);

	public void removeCustomer(int id);
}

package com.app.spring.dao.impl;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import com.app.spring.dao.CustomerDAO;
import com.app.spring.model.Customer;

/**
 * 
 * @author malalanayake
 *
 */
@Repository
public class CustomerDAOImpl implements CustomerDAO {

	private static final Logger logger = LoggerFactory.getLogger(CustomerDAOImpl.class);

	private SessionFactory sessionFactory;

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

	@Override
	public void addCustomer(Customer p) {
		Session session = this.sessionFactory.getCurrentSession();
		session.persist(p);
		logger.info("Customer saved successfully, Customer Details=" + p);
	}

	@Override
	public void updateCustomer(Customer p) {
		Session session = this.sessionFactory.getCurrentSession();
		session.update(p);
		logger.info("Customer updated successfully, Person Details=" + p);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Customer> listCustomers() {
		Session session = this.sessionFactory.getCurrentSession();
		List<Customer> customersList = session.createQuery("from Customer").list();
		for (Customer c : customersList) {
			logger.info("Customer List::" + c);
		}
		return customersList;
	}

	@Override
	public Customer getCustomerById(int id) {
		Session session = this.sessionFactory.getCurrentSession();
		Customer c = (Customer) session.load(Customer.class, new Integer(id));
		logger.info("Customer loaded successfully, Customer details=" + c);
		return c;
	}

	@Override
	public void removeCustomer(int id) {
		Session session = this.sessionFactory.getCurrentSession();
		Customer c = (Customer) session.load(Customer.class, new Integer(id));
		if (null != c) {
			session.delete(c);
		}
		logger.info("Customer deleted successfully, Customer details=" + c);
	}

}

If we are using the hibernate we have to have start the transaction before each operation and commit the transaction after the work done. If we are not doing these things our data is not going to persist in DB. But you can see I have not started transaction in explicitly. This is the point that AOP come to the picture. Lets look at the service layer you can see I have declare the @Transactional annotation. That means I wanted to start the transaction before going to execute the operations. This transaction handling mechanism is going to handle by the Spring frame work. We don’t want to worry about that. Only we need to concern about the spring configurations.

package com.app.spring.service;

import java.util.List;

import com.app.spring.model.Customer;

/**
 * 
 * @author malalanayake
 *
 */
public interface CustomerService {

	public void addCustomer(Customer p);

	public void updateCustomer(Customer p);

	public List<Customer> listCustomers();

	public Customer getCustomerById(int id);

	public void removeCustomer(int id);

}
package com.app.spring.service.impl;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.app.spring.dao.CustomerDAO;
import com.app.spring.model.Customer;
import com.app.spring.service.CustomerService;

/**
 * 
 * @author malalanayake
 *
 */
@Service
public class CustomerServiceImpl implements CustomerService {

	private CustomerDAO customerDAO;

	public void setCustomerDAO(CustomerDAO customerDAO) {
		this.customerDAO = customerDAO;
	}

	@Override
	@Transactional
	public void addCustomer(Customer c) {
		this.customerDAO.addCustomer(c);
	}

	@Override
	@Transactional
	public void updateCustomer(Customer c) {
		this.customerDAO.updateCustomer(c);
	}

	@Override
	@Transactional
	public List<Customer> listCustomers() {
		return this.customerDAO.listCustomers();
	}

	@Override
	@Transactional
	public Customer getCustomerById(int id) {
		return this.customerDAO.getCustomerById(id);
	}

	@Override
	@Transactional
	public void removeCustomer(int id) {
		this.customerDAO.removeCustomer(id);
	}

}

Now lock at the servlet-context.xml this file is the most important part in the spring framework.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<!-- Enables the Spring MVC annotations ex/ @Controller -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving 
		up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources 
		in the /WEB-INF/views directory -->
	<beans:bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

	<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<beans:property name="url"
			value="jdbc:mysql://localhost:3306/TestDB" />
		<beans:property name="username" value="root" />
		<beans:property name="password" value="root123" />
	</beans:bean>

	<!-- Hibernate 4 SessionFactory Bean definition -->
	<beans:bean id="hibernate4AnnotatedSessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<beans:property name="dataSource" ref="dataSource" />
		<beans:property name="annotatedClasses">
            <beans:list>
                <beans:value>com.app.spring.model.Customer</beans:value>
            </beans:list>
        </beans:property>
		<beans:property name="hibernateProperties">
			<beans:props>
				<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
				</beans:prop>
				<beans:prop key="hibernate.show_sql">true</beans:prop>
				<beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
			</beans:props>
		</beans:property>
	</beans:bean>
	
	<!-- Inject the transaction manager  -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
	<beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<beans:property name="sessionFactory" ref="hibernate4AnnotatedSessionFactory" />
	</beans:bean>
	
	<!-- Inject the instance to customerDAO reference with adding sessionFactory -->
	<beans:bean id="customerDAO" class="com.app.spring.dao.impl.CustomerDAOImpl">
		<beans:property name="sessionFactory" ref="hibernate4AnnotatedSessionFactory" />
	</beans:bean>
	<!-- Inject the instance to service reference with adding customerDao instance -->
	<beans:bean id="customerService" class="com.app.spring.service.impl.CustomerServiceImpl">
		<beans:property name="customerDAO" ref="customerDAO"></beans:property>
	</beans:bean>
	<!-- Set the package where the annotated classes located at ex @Controller -->
	<context:component-scan base-package="com.app.spring" />


</beans:beans>

Look at the following two three lines. We are going to discuss about the dependency injection.

<beans:bean id="customerDAO" class="com.app.spring.dao.impl.CustomerDAOImpl">
		<beans:property name="sessionFactory" ref="hibernate4AnnotatedSessionFactory" />
	</beans:bean>
	<!-- Inject the instance to service reference with adding customerDao instance -->
	<beans:bean id="customerService" class="com.app.spring.service.impl.CustomerServiceImpl">
		<beans:property name="customerDAO" ref="customerDAO"></beans:property>
	</beans:bean>

In our CustomerDAOImpl class we have the reference of sessionFactory but we are not creating any instance of session factory rather having setter method for that. So that means some how we need to pass the reference to initiate the session factory. To archive that task we need to say that spring framework to create the instance of sessionFactory. If you follow the configuration above you can see how I declared that.

Another thing is if you declare something as property that means it is going to inject the instance by using setter method and you have to have setter method for that particular reference (Go and see the CustomerDAOImpl class).

Lets look at CustomerServiceImpl class, I have declare the CustomerDAO reference with the setter method. So that means we can inject the CuatomerDAOImpl reference same procedure as we did for the CustomerDAOImpl class.

It is really easy but you have to set the configuration properly.

Deployment descriptor web.xml
You have to set the context configuration as follows.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

Controller class

package com.app.spring.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.app.spring.model.Customer;
import com.app.spring.service.CustomerService;

/**
 * 
 * @author malalanayake
 *
 */
@Controller
public class CustomerController {

	private CustomerService customerService;

	@Autowired(required = true)
	@Qualifier(value = "customerService")
	public void setPersonService(CustomerService cs) {
		this.customerService = cs;
	}

	@RequestMapping(value = "/customers", method = RequestMethod.GET)
	public String listCustomers(Model model) {
		model.addAttribute("customer", new Customer());
		model.addAttribute("listCustomers", this.customerService.listCustomers());
		return "customer";
	}

	// For add and update person both
	@RequestMapping(value = "/customer/add", method = RequestMethod.POST)
	public String addCustomer(@ModelAttribute("customer") Customer c) {

		if (c.getId() == 0) {
			// new person, add it
			this.customerService.addCustomer(c);
		} else {
			// existing person, call update
			this.customerService.updateCustomer(c);
		}

		return "redirect:/customers";

	}

	@RequestMapping("/customer/remove/{id}")
	public String removeCustomer(@PathVariable("id") int id) {

		this.customerService.removeCustomer(id);
		return "redirect:/customers";
	}

	@RequestMapping("/customer/edit/{id}")
	public String editCustomer(@PathVariable("id") int id, Model model) {
		model.addAttribute("customer", this.customerService.getCustomerById(id));
		model.addAttribute("listCustomers", this.customerService.listCustomers());
		return "customer";
	}

}

Now I’m going to talk about MVC configuration. Look at controller class. I have declare the request mappings by using the annotation @RequestMapping. This is how we redirect the request to the particular service which is backing on service layer. Then we need to inject the data to the model and send that model to the view.

You can see in our project structure we have customer.jsp on /WEB-INF/views folder. We need to let the view resolver to know that our views are located at this folder. That is why we are doing the following configuration.

<!-- Resolves views selected for rendering by @Controllers to .jsp resources 
		in the /WEB-INF/views directory -->
	<beans:bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

See the CustomerController class that I have return the string like “customer”.

@RequestMapping(value = "/customers", method = RequestMethod.GET)
	public String listCustomers(Model model) {
		model.addAttribute("customer", new Customer());
		model.addAttribute("listCustomers", this.customerService.listCustomers());
		return "customer";
	}

Once I return the string “customer” spring frame work knows it is a view name. Then it will pic the view as follows according to the configuration.
/WEB-INF/views/customer.jsp

Finally I have used the JSTL tags, spring core and spring form tags in customer.jsp to represent the data came from model class.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ page session="false"%>
<html>
<head>
<title>Manage Customer</title>
<style type="text/css">
.tg {
	border-collapse: collapse;
	border-spacing: 0;
	border-color: #ccc;
}

.tg td {
	font-family: Arial, sans-serif;
	font-size: 14px;
	padding: 10px 5px;
	border-style: solid;
	border-width: 1px;
	overflow: hidden;
	word-break: normal;
	border-color: #ccc;
	color: #333;
	background-color: #fff;
}

.tg th {
	font-family: Arial, sans-serif;
	font-size: 14px;
	font-weight: normal;
	padding: 10px 5px;
	border-style: solid;
	border-width: 1px;
	overflow: hidden;
	word-break: normal;
	border-color: #ccc;
	color: #333;
	background-color: #8FBC8F;
}

.tg .tg-4eph {
	background-color: #f9f9f9
}
</style>
</head>
<body>
	<h1>Manage Customers</h1>

	<c:url var="addAction" value="/customer/add"></c:url>

	<form:form action="${addAction}" commandName="customer">
		<table>
			<c:if test="${!empty customer.name}">
				<tr>
					<td><form:label path="id">
							<spring:message text="ID" />
						</form:label></td>
					<td><form:input path="id" readonly="true" size="8"
							disabled="true" /> <form:hidden path="id" /></td>
				</tr>
			</c:if>
			<tr>
				<td><form:label path="name">
						<spring:message text="Name" />
					</form:label></td>
				<td><form:input path="name" /></td>
			</tr>
			<tr>
				<td><form:label path="address">
						<spring:message text="Address" />
					</form:label></td>
				<td><form:input path="address" /></td>
			</tr>
			<tr>
				<td colspan="2"><c:if test="${!empty customer.name}">
						<input type="submit"
							value="<spring:message text="Edit Customer"/>" />
					</c:if> <c:if test="${empty customer.name}">
						<input type="submit" value="<spring:message text="Add Customer"/>" />
					</c:if></td>
			</tr>
		</table>
	</form:form>
	<br>
	<h3>Customer List</h3>
	<table class="tg">
		<tr>
			<th width="80">Customer ID</th>
			<th width="120">Customer Name</th>
			<th width="120">Customer Address</th>
			<th width="60">Edit</th>
			<th width="60">Delete</th>
		</tr>
		<c:if test="${!empty listCustomers}">
			<c:forEach items="${listCustomers}" var="customer">
				<tr>
					<td>${customer.id}</td>
					<td>${customer.name}</td>
					<td>${customer.address}</td>
					<td><a href="<c:url value='/customer/edit/${customer.id}' />">Edit</a></td>
					<td><a
						href="<c:url value='/customer/remove/${customer.id}' />">Delete</a></td>
				</tr>
			</c:forEach>
		</c:if>
	</table>

</body>
</html>

Now build and deploy the war on tomcat and go to the following url http://localhost:8080/SampleSpringMVCHibernate/customers

Screen Shot 2014-07-26 at 6.29.42 PM
Screen Shot 2014-07-26 at 6.31.10 PM
Screen Shot 2014-07-26 at 6.31.24 PM

Advantages of Dependency injection
1. Loosely couple architecture.
2. Separation of responsibility.
3. Configuration and code is separate.
4. Using configuration, a different implementation can be supplied without changing the dependent code.
5. Testing can be performed using mock objects.

Advantages of using Object relational mapping framework
1. Business code access objects rather than DB tables.
2. Hides details of SQL queries from OO logic.
3. Baking by JDBC
4. No need to play with database implementation.
5. Transaction management and automatic key generation.
6. Fast development of application.

I hope you got the idea about how spring framework is working.

 
5 Comments

Posted by on July 27, 2014 in java, spring

 

Tags: , , ,

 
%d bloggers like this: