Integration Spring MVC3 and MongoDB


Hi, In this tutorial we will discuss Soring MVC 3 with NoSQL database MongoDB. In previous chapters you have seen the CRUD application using Spring MVC3 with relational database MYSQL.

MongoDB, noSQL open source database, written in C++, with many great features like map-reduce , auto sharding, replication, high availability and etc.

Introduction-

With the explosion in the amounts of data being generated in recent years, more and more organisations are looking at alternative data storage options to the traditional relational model. This in turn has lead to huge growth in the NoSQL space, with leading web companies such as Facebook, Google, Twitter, Reddit, etc adopting NoSQL solutions.

Within the NoSQL space there are several different implementation options (Graph based DBs such as Neo4J, Wide Column DBS such as Cassandra and Haddop, Document based DBs such as MongoDB and CouchDB) and careful consideration is needed before choosing an implementation.

This article will look at integrating the Document oriented database MongoDB with a Spring MVC Web Application – it is important to note that due to the nature of Document based storage solutions, they are not applicable for all problems. For example, if the data you are modelling cannot naturally be stored as “documents”, then a Document-oriented DB probably isn’t the best solution (an easy way to think about whether the model can be stored as a document is to think of it being stored on paper – does it make sense for all the elements to be intrinsically grouped in to a single document? E.g. if you were storing an essay, all the chapters are intrinsically linked to the essay as a whole document, and a single chapter doesn’t make much sense as an individual object on its own).

What is MongoDB?
MongoDB (from "humongous") is a scalable, high-performance, open source, document-oriented database. Written in C++, MongoDB features:
  • Document-oriented storage
  • Full Index Support
  • Replication & High Availability
  • Scale horizontally without compromising functionality.
  • Rich, document-based queries.
  • Atomic modifiers for contention-free performance.
  • Flexible aggregation and data processing.
  • Store files of any size without complicating your stack.
  • Enterprise class support, training, and consulting available.

What is Spring Data - Document?
The Spring Data Document (or DATADOC) framework makes it easy to write Spring applications that use a Document store by eliminating the redundant tasks and boiler place code required for interacting with the store through Spring's excellent infrastructure support.

Getting Started-

This article will not cover details of creating a Spring MVC web application, and will assume a prior knowledge of the Spring MVC framework and core Spring principles. The example application itself is very simple, and there are several aspects of it that have not been included (these have been intentionally left out for simplicity), such as security/authentication features, advanced UI/screen flow/editing, etc, the purpose of the example application is purely to demonstrate MongoDB integration with Spring.

Our application is a simple CRUD system for managing a list of Employees. Data is stored in MongoDB database. We'll start by declaring our domain objects. Then we'll discuss the service layer. And lastly we'll add the controllers.
Building the Domain Model-
Here we are defining the Domain Model for application it is quite similar to the other domain model using in the previous CRUD chapter with Hibernate. Here we used @Document Annotation in stead of @Entity annotation in the previous example.

One of the advantages of using Document-oriented DBs is that as it is schema-less, objects can be added to the document in the future without affecting existing data.

We model these objects as simple POJOs, just like modelling entities in JPA, but there are a few simple annotations Spring-Data provides for us. Our application contains a single domain object named Employee. It consists the following properties:
Employee.java
package com.dineshonjava.model;

import java.io.Serializable;

import org.springframework.data.mongodb.core.mapping.Document;


/**
 * A simple POJO representing a Employee
 * @author Dinesh Rajput
 *
 */
@Document(collection="employee")
public class Employee implements Serializable{

 private static final long serialVersionUID = -723583058586873479L;
 
 private String empId;
 private String empName;
 private String empAddress;
 private String salary;
 private String empAge;
 
 public String getEmpId() {
  return empId;
 }
 public void setEmpId(String empId) {
  this.empId = empId;
 }
 public String getEmpName() {
  return empName;
 }
 public void setEmpName(String empName) {
  this.empName = empName;
 }
 public String getEmpAddress() {
  return empAddress;
 }
 public void setEmpAddress(String empAddress) {
  this.empAddress = empAddress;
 }
 public String getSalary() {
  return salary;
 }
 public void setSalary(String salary) {
  this.salary = salary;
 }
 public String getEmpAge() {
  return empAge;
 }
 public void setEmpAge(String empAge) {
  this.empAge = empAge;
 }

}

We use the @Document and @Id annotation to indicate any object that we want to treat as a Document, and the ID.
EmployeeBean.java
package com.dineshonjava.bean;

/**
 * @author Dinesh Rajput
 *
 */
public class EmployeeBean {
 private String id;
 private String name;
 private String age;
 private String salary;
 private String address;
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getAge() {
  return age;
 }
 public void setAge(String age) {
  this.age = age;
 }
 public String getSalary() {
  return salary;
 }
 public void setSalary(String salary) {
  this.salary = salary;
 }
 public String getAddress() {
  return address;
 }
 public void setAddress(String address) {
  this.address = address;
 }
 
 
}
Data Access Layer-
Now we have our data model we need to create our Data Access layer, so we can easily perform CRUD updates on our documents. Creating our Data Access Objects is incredibly simple using MongoDB’s MongoRepository, and we automatically get basic CRUD functionality by just extending that interface.

The Service Layer-
Our service class contains the main changes in the original application. Instead of calling native MongoDB methods for performing CRUD operations, we use Spring Data's MongoTemplate instead.

What is MongoTemplate?

The template offers convenience methods and automatic mapping between MongoDB JSON documents and your domain classes. Out of the box, MongoTemplate uses a Java-based default converter but you can also write your own converter classes to be used for reading and storing domain objects.

EmployeeService.java
package com.dineshonjava.service;

import java.util.List;

import com.dineshonjava.model.Employee;

/**
 * @author Dinesh Rajput
 *
 */
public interface EmployeeService {
 
 public Boolean addEmployee(Employee employee);

 public List<Employee> listEmployeess();
 
 public Employee getEmployee(String empid);
 
 public Boolean deleteEmployee(String empid);
}


EmployeeServiceImpl.java
package com.dineshonjava.service;

import java.util.List;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.dineshonjava.model.Employee;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;

/**
 * Service for processing {@link Employee} objects.
 * Uses Spring's {@link MongoTemplate} to perform CRUD operations.
 * * For a complete reference to MongoDB
 * see http://www.mongodb.org/
 * 

* For a complete reference to Spring Data MongoDB
 * see http://www.springsource.org/spring-data
 * 
 * @author Dinesh Rajput
 *
 */
@Service("employeeService")
@Transactional
public class EmployeeServiceImpl implements EmployeeService {

 @Autowired(required=true)
 private MongoTemplate mongoTemplate;
// 
// @Autowired
// private MongoOperations mongoOperations;
 
 protected static Logger logger = Logger.getLogger("service");
   
  /**
   * Retrieves all Employees
   */
  public List<Employee> listEmployeess() {
   logger.debug("Retrieving all Employees");
   
    // Execute the query and find all matching entries
   List<Employee> employees = mongoTemplate.findAll(Employee.class);
  
   return employees;
  }
   
  /**
   * Retrieves a single Employee
   */
  public Employee getEmployee(String empid ) {
   logger.debug("Retrieving an existing Employee");
   Employee employee = new Employee();
   // Find an entry where empid matches the id
   DBObject query = new BasicDBObject();
   query.put("empId", empid);
   DBObject cursor = mongoTemplate.getDb().getCollection("employee").findOne(query);
   employee.setEmpId(cursor.get("empId").toString());
   employee.setEmpName(cursor.get("empName").toString());
   employee.setEmpAge(cursor.get("empAge").toString());
      employee.setSalary(cursor.get("salary").toString());
   employee.setEmpAddress(cursor.get("empAddress").toString());
       
   return employee;
  }
   
  /**
   * Adds a new Employee
   */
  public Boolean addEmployee(Employee employee) {
   logger.debug("Adding a new employee");
    
 try {
 if(employee.getEmpId() != null && employee.getEmpId() != ""){
   // Find an entry where empid matches the id
   DBObject query1 = new BasicDBObject();
         query1.put("empId", employee.getEmpId());
         DBObject query = new BasicDBObject();
   query.put("empId", employee.getEmpId());
  query.put("empName", employee.getEmpName());
   query.put("empAge", employee.getEmpAge());
   query.put("salary", employee.getSalary());
         query.put("empAddress", employee.getEmpAddress());
        mongoTemplate.getDb().getCollection("employee").update(query1, query);
     }else{
         // Set a new value to the empid property first since it's blank
   employee.setEmpId(UUID.randomUUID().toString());
    // Insert to db
    mongoTemplate.save(employee);
         }
   
      return true;
   } 
   catch (Exception e) {
   logger.error("An error has occurred while trying to add new employee", e);
   return false;
   }
  }
   
  /**
   * Deletes an existing employee
   */
   public Boolean deleteEmployee(String empid) {
  logger.debug("Deleting existing employee");
    
     try {
     
 // Find an entry where empid matches the id
  DBObject query = new BasicDBObject();
  query.put("empId", empid);
  // Run the query and delete the entry
  mongoTemplate.getDb().getCollection("employee").findAndRemove(query);
         
    return true;
 } catch (Exception e) {
  logger.error("An error has occurred while trying to delete new employee", e);
  return false;
  }
   }
}



Spring Configuration-
To use Spring's MongoTemplate it needs to be declared via configuration. It also needs a reference to a MongoDB database. Let's declare an XML configuration that satisfies these requirements:
mongo-config.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:mongo="http://www.springframework.org/schema/data/mongo"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/data/mongo
      http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">
  
 <!-- Default bean name is 'mongo' -->
 <mongo:mongo host="localhost" port="27017"/>
  <!-- Default bean name is 'mongo' -->
 <mongo:mongo>
  <mongo:options connections-per-host="100"
   threads-allowed-to-block-for-connection-multiplier="5"
            max-wait-time="120000000"
            connect-timeout="10000000"
            socket-keep-alive="true"
            socket-timeout="15000000"
            auto-connect-retry="true"/>
 </mongo:mongo>
 <!-- Offers convenience methods and automatic mapping between MongoDB JSON documents and your domain classes. -->
   <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
       <constructor-arg ref="mongo"/>
   <constructor-arg name="databaseName" value="mydb"/>
   </bean>
    
</beans>
Notice we're using the mongo namespace:

xmlns:mongo="http://www.springframework.org/schema/data/mongo"

We've declared a reference to a MongoDB database by declaring:
<mongo:mongo host="localhost" port="27017"/>
Then we declared a MongoTemplate that references a MongoDB database (mongo), a database (dineshonjavadb):

       <constructor-arg ref="mongo"/>
         <constructor-arg name="databaseName" value="dineshonjavadb"/>
   
The Controller Layer-
After creating the domain and service classes, we need to declare a controller that will handle the web requests.
EmployeeController.java
package com.dineshonjava.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.dineshonjava.bean.EmployeeBean;
import com.dineshonjava.model.Employee;
import com.dineshonjava.service.EmployeeService;

/**
 * @author Dinesh Rajput
 *
 */
@Controller
public class EmployeeController {
 
 @Autowired
 private EmployeeService employeeService;
 
 @RequestMapping(value = "/save", method = RequestMethod.POST)
 public ModelAndView saveEmployee(@ModelAttribute("command") EmployeeBean employeeBean, BindingResult result) {
  Employee employee = prepareModel(employeeBean);
  employeeService.addEmployee(employee);
  return new ModelAndView("redirect:/add.html");
 }

 @RequestMapping(value="/employees", method = RequestMethod.GET)
 public ModelAndView listEmployees() {
  Map<String, Object> model = new HashMap<String, Object>();
  model.put("employees",  prepareListofBean(employeeService.listEmployeess()));
  return new ModelAndView("employeesList", model);
 }

 @RequestMapping(value = "/add", method = RequestMethod.GET)
 public ModelAndView addEmployee(@ModelAttribute("command")  EmployeeBean employeeBean,BindingResult result) {
  Map<String, Object> model = new HashMap<String, Object>();
  model.put("employees",  prepareListofBean(employeeService.listEmployeess()));
  return new ModelAndView("addEmployee", model);
 }
 
 @RequestMapping(value = "/index", method = RequestMethod.GET)
 public ModelAndView welcome() {
  return new ModelAndView("index");
 }
 
 @RequestMapping(value = "/delete", method = RequestMethod.GET)
 public ModelAndView editEmployee(@ModelAttribute("command")  EmployeeBean employeeBean, BindingResult result) {
  employeeService.deleteEmployee(employeeBean.getId());
  Map<String, Object> model = new HashMap<String, Object>();
  model.put("employee", null);
  model.put("employees",  prepareListofBean(employeeService.listEmployeess()));
  return new ModelAndView("addEmployee", model);
 }
 
 @RequestMapping(value = "/edit", method = RequestMethod.GET)
 public ModelAndView deleteEmployee(@ModelAttribute("command")  EmployeeBean employeeBean, BindingResult result) {
  Map<String, Object> model = new HashMap<String, Object>();
  model.put("employee", prepareEmployeeBean(employeeService.getEmployee(employeeBean.getId())));
  model.put("employees",  prepareListofBean(employeeService.listEmployeess()));
  return new ModelAndView("addEmployee", model);
 }
 
 private Employee prepareModel(EmployeeBean employeeBean){
  Employee employee = new Employee();
  employee.setEmpAddress(employeeBean.getAddress());
  employee.setEmpAge(employeeBean.getAge().toString());
  employee.setEmpName(employeeBean.getName());
  employee.setSalary(employeeBean.getSalary().toString());
  employee.setEmpId(employeeBean.getId().toString());
  employeeBean.setId(null);
  return employee;
 }
 
 private List<EmployeeBean> prepareListofBean(List<Employee> employees){
  List<EmployeeBean> beans = null;
  if(employees != null && !employees.isEmpty()){
   beans = new ArrayList<EmployeeBean>();
   EmployeeBean bean = null;
   for(Employee employee : employees){
    bean = new EmployeeBean();
    bean.setName(employee.getEmpName());
    bean.setId(employee.getEmpId());
    bean.setAddress(employee.getEmpAddress());
    bean.setSalary(employee.getSalary().toString());
    bean.setAge(employee.getEmpAge());
    beans.add(bean);
   }
  }
  return beans;
 }
 
 private EmployeeBean prepareEmployeeBean(Employee employee){
  EmployeeBean bean = new EmployeeBean();
  bean.setAddress(employee.getEmpAddress());
  bean.setAge(employee.getEmpAge());
  bean.setName(employee.getEmpName());
  bean.setSalary(employee.getSalary());
  bean.setId(employee.getEmpId());
  return bean;
 }
}

Other Configurations and Files-
sdnext-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:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx"
 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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<import resource="mongo-config.xml"/>
 <context:component-scan base-package="com.dineshonjava" />

 <!-- <tx:annotation-driven transaction-manager="hibernateTransactionManager"/> -->

 <bean id="jspViewResolver"
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="viewClass"
   value="org.springframework.web.servlet.view.JstlView" />
  <property name="prefix" value="/WEB-INF/views/" />
  <property name="suffix" value=".jsp" />
 </bean>

</beans>
web.xml
<?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">

 <servlet>
  <servlet-name>sdnext</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/sdnext-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>sdnext</servlet-name>
  <url-pattern>*.html</url-pattern>
 </servlet-mapping>

 <welcome-file-list>
  <welcome-file>index.html</welcome-file>
 </welcome-file-list>

</web-app>

Download Source Code + Libs
SpringMVC3MongoDB.zip




2 comments:

  1. HI Dinesh can u provide a download link for this code

    ReplyDelete
    Replies
    1. Hi friend,

      this chapter little incomplete right now please find download material. As soon as I will complete it.

      Thanks,
      Dinesh

      Delete