Showing posts with label NoSQL. Show all posts
Showing posts with label NoSQL. Show all posts

Thursday, March 8, 2012

Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 1)

Introduction

In this tutorial, we will create a simple CRUD application using Spring 3.1 and Neo4j. We will based this tutorial on a previous guide for MongoDB. This means we will re-use our existing design and implement only the data layer to use Neo4j as our data store.


Dependencies

  • Spring core 3.1.0.RELEASE
  • Spring Data Neo4j 2.1.0.M1
  • Neo4j Kernel 1.6
  • Neo4j Cypher 1.6
  • Neo4j Cypher DSL 1.6
  • See pom.xml for details

Github

To access the source code, please visit the project's Github repository (click here)

Functional Specs

Before we start, let's define our application's specification as follows:
  • A CRUD page for managing users
  • Use AJAX to avoid page refresh
  • Users have roles: either admin or regular (default)
  • Everyone can create new users and edit existing ones
  • When editing, users can only edit first name, last name, and role fields
  • A username is assumed to be unique

Here's our Use Case diagram:
[User]-(View)
[User]-(Add) 
[User]-(Edit) 
[User]-(Delete) 

Database

If you're new to Neo4j and Spring Data Neo4j and coming from a SQL-background, please take some time to read the following references:

In its simplest form, Neo4j is a NoSQL graph database containing nodes and relationships.

What is Neo4j?

The Neo4j data model consists of nodes and relationships, both of which can have key/value-style properties. What does that mean, exactly? Nodes are the graph database name for records, with property keys instead of column names. That's normal enough. Relationships are the special part. In Neo4j, relationships are first-class citizens, meaning they are more than a simple foreign-key reference to another record, relationships carry information.

Source: Spring Data Neo4j - Chapter 4. Learning Neo4j

What is a Graph database?

A graph database stores data in a graph, the most generic of data structures, capable of elegantly representing any kind of data in a highly accessible way.

“A Graph —records data in→ Nodes —which have→ Properties”

“Nodes —are organized by→ Relationships —which also have→ Properties”

“A Traversal —navigates→ a Graph; it —identifies→ Paths —which order→ Nodes”

“An Index —maps from→ Properties —to either→ Nodes or Relationships”

“A Graph Database —manages a→ Graph and —also manages related→ Indexes”


Source: What is a Graph Database?

From Java to Neo4j

We have two Java classes representing our domain: User and Role. Here is the Class diagram:

# Cool UML Diagram
[User|id;firstName;lastName;username;password;role{bg:orange}]1--1> [Role|id;role{bg:green}]

In Neo4j, User and Role are nodes, and the link between the two is a relationship. In Part 2 we will demonstrate how to declare them as nodes and how to create the relationship.

Here's the Activity diagram:

http://yuml.me/diagram/activity/(start)-%3E%3Cd1%3Eview-%3E(Show%20Records)-%3E%7Ca%7C-%3E(end),%20%3Cd1%3Eadd-%3E(Show%20Form)-%3E%7Ca%7C,%20%3Cd1%3Eedit-%3E%3Cd2%3Ehas%20selected-%3E(Show%20Form)-%3E%7Ca%7C,%20%3Cd2%3Eno%20record%20selected-%3E(Popup%20Alert)-%3E%7Ca%7C,%20%3Cd1%3Edelete-%3E%3Cd3%3Ehas%20selected-%3E(Delete%20Record)-%3E%7Ca%7C,%20%3Cd3%3Eno%20record%20selected-%3E(Popup%20Alert)-%3E%7Ca%7C.

Screenshots

Let's preview how the application will look like after it's finished. This is also a good way to clarify further our application's specs. Note: These are the same screenshots you will see from the Spring MVC 3.1 - Implement CRUD with Spring Data MongoDB guide (We're reusing the exact presentation layer).

Entry page
The entry page is the primary page that users will see. It contains a table showing user records and four buttons for adding, editing, deleting, and reloading data. All interactions will happen in this page.

Entry page

Edit existing record
When user clicks the Edit button, an Edit Record form shall appear after the table.

Edit record form

When a user submits the form, a success or failure alert should appear.

Success alert

When the operation is successful, the update record should reflect on the table.

Edited record appears on the table

Create new record
When a user clicks the New button, a Create New Record form shall appear after the table.

Create new record form

When a user submits the form, a success or failure alert should appear.

Success alert

When the operation is successful, the new record should appear on the table.

New record shows on the form

Delete record
When user clicks the Delete button, a success or failure alert should appear.

Success alert

Reload record
When user clicks the Reload button, the data on the table should be reloaded.

Errors
When user clicks the Edit or Delete button without selecting a record first, a "Select a record first!" alert should appear.

Error alert

Next

In the next section, we will write the Java classes and discuss the application's layers. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 1) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 2)

Review

In the previous section, we have laid down the functional specs of the application and discussed the Neo4j data model. In this section, we will discuss the project's structure, write the Java classes, and map our domain classes to the Neo4j data model.


Project Structure

Our application is a Maven project and therefore follows Maven structure. As we create the classes, we've organized them in logical layers: domain, repository, service, and controller.

Here's a preview of our project's structure:



The Layers

Domain Layer

This layer contains the following POJOs:
  • User - represents our user
  • Role - represents the role of the user
  • UserRoleRelationship - represents the relationship of a user to its role
  • .





Notice we've annotated our domain classes with Spring Data Neo4j annotations. Here are the explanations:

@NodeEntity

The @NodeEntity annotation is used to turn a POJO class into an entity backed by a node in the graph database. Fields on the entity are by default mapped to properties of the node. Fields referencing other node entities (or collections thereof) are linked with relationships.

Source: Spring Data Neo4j Reference

@GraphId

For the simple mapping this is a required field which must be of type Long. It is used by Spring Data Neo4j to store the node or relationship-id to re-connect the entity to the graph.

For the advanced mapping such a field is optional. Only if the underlying id has to be accessed, it is needed.

Source: Spring Data Neo4j Reference

@Indexed

@Indexed: Making entities searchable by field value

The @Indexed annotation can be declared on fields that are intended to be indexed by the Neo4j indexing facilities. The resulting index can be used to later retrieve nodes or relationships that contain a certain property value, e.g. a name. Often an index is used to establish the start node for a traversal.

Source: Spring Data Neo4j Reference

@Fetch

To have the collections of relationships being read eagerly ... we have to annotate it with the @Fetch annotation. Otherwise Spring Data Neo4j refrains from following relationships automatically.

Source: Spring Data Neo4j Reference

@RelatedTo

@RelatedTo: Connecting node entities

Every field of a node entity that references one or more other node entities is backed by relationships in the graph. These relationships are managed by Spring Data Neo4j automatically.

The simplest kind of relationship is a single field pointing to another node entity (1:1). In this case, the field does not have to be annotated at all, although the annotation may be used to control the direction and type of the relationship. When setting the field, a relationship is created when the entity is persisted. If the field is set to null, the relationship is removed.

Source: Spring Data Neo4j Reference

@RelationshipEntity

To access the full data model of graph relationships, POJOs can also be annotated with @RelationshipEntity, making them relationship entities. Just as node entities represent nodes in the graph, relationship entities represent relationships. As described above, fields annotated with @RelatedTo provide a way to link node entities together via relationships, but it provides no way of accessing the relationships themselves.

Relationship entities can be accessed via by @RelatedToVia-annotated

Fields in relationship entities are, similarly to node entities, persisted as properties on the relationship. For accessing the two endpoints of the relationship, two special annotations are available: @StartNode and @EndNode. A field annotated with one of these annotations will provide read-only access to the corresponding endpoint, depending on the chosen annotation.

Source: Spring Data Neo4j Reference

Controller Layer

This layer contains two controllers, MediatorController and UserController
  • MediatorController is responsible for redirecting requests to appropriate pages. This isn't really required but it's here for organizational purposes.
  • UserController is responsible for handling user-related requests such as adding and deleting of records



Service Layer

This layer contains a single service, UserService for managing users.



What is Spring Data Neo4j?

Spring Data Neo4j enables POJO based development for the Graph Database Neo4j. It maps annotated entity classes to the Neo4j Graph Database with advanced mapping functionality. The template programming model is equivalent to well known Spring templates and builds the basis for interaction with the graph and is also used for the advanced repository support.
Spring Data Neo4j is part of the Spring Data project which aims to provide convenient support for NoSQL databases.

Source: http://www.springsource.org/spring-data/neo4j

Utility classes

TraceInterceptor class is an AOP-based utility class to help us debug our application. This is a subclass of CustomizableTraceInterceptor (see Spring Data JPA FAQ)



Next

In the next section, we will focus on the configuration files for enabling Spring MVC. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 2) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 3)

Review

In the previous section, we have implemented the Java classes and discussed the Neo4j service. In this section, we will write the configuration files for enabling Spring MVC and Neo4j support.


Configuration

To enable Neo4j support, we need to declare the following beans
  • a Spring Data Neo4j repository
  • a Neo4j store directory
  • Optionally, we declared an InitNeo4jService to automatically populate our database with sample data (currently disabled)


Finally, here's our applicationContext.xml file

Next

In the next section, we will create the HTML and JavaScript files. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 3) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 4)

Review

In the previous section, we have created the configuration files and discussed them accordingly. In this section, we will focus on the view layer, in particular the HTML files and JavaScript codes.


HTML Files (with AJAX)

To improve the user experience, we will use AJAX to make the application responsive. All actions will be performed on the same page (no page refresh). Consequently, we only have one HTML page for the entire application. The rest are JavaScript files.

Notice how we've structured the HTML page. Basically, we followed the concept of separation of concerns by separating markup tags, CSS, and JavaScript code. We tried as much as possible to make the JavaScript code and CSS styles unobtrusive.



A Closer Look

At first glance, this JSP file seems complex. On the contrary, it's quite simple. Let's break it into smaller pieces for clarity:

URLs
The following declares our URLs as mapped to our UserController. We're using the url taglib to make the URL portable.


Imports
Here we're importing custom CSS and JavaScript files, along with jQuery.


What is jQuery?
jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.

Source: http://jquery.com/

JavaScript initialization
Here we're preparing the URLs, attaching functions to our buttons, and initially loading the table. The main chunk of the JavaScript code is referenced from an external JavaScript file custom.js
  • loadTable(): Performs an AJAX request and populates our table with records
  • toggleForms(): Hides and shows specific forms based on the passed argument
  • toggleCrudButtons(): Hides and shows buttons
  • hasSelected(): Checks whether a record has been selected
  • fillEditForm(): Fills the Edit form with details based on the selected record
  • submitDeleteRecord(): Submits a delete request via AJAX
  • submitNewRecord(): Submits a create new record request via AJAX
  • submitUpdateRecord(): Submits an update record request via AJAX


Table and buttons
This is a simple table for displaying records. The buttons are for interacting with the data.


Forms
These are forms used when adding and editing records.


Preview

If we run our application, this is what we shall see:

Entry page

For more screenshots, please visit Part 1 of this tutorial and check the Screenshots section.

Next

In the next section, we will build and run the application using Maven, and show how to import the project in Eclipse. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 4) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 5)

Review

We have just completed our application! In the previous sections, we have discussed the functional specs, created the Java classes, declared the configuration files, and wrote the HTML files. In this section, we will build and run the application using Maven, and show how to import the project in Eclipse.


Running the Application

Access the source code

To download the source code, please visit the project's Github repository (click here)

Preparing the data source

  • You don't need to run a separate Neo4j server because it will start simultaneously with the application.
  • There's no need to populate the database.
  • Due to some issues with the initialization service, I have decided to deactivate the service (though it's included in the source code for reference).

Building with Maven

  1. Ensure Maven is installed
  2. Open a command window (Windows) or a terminal (Linux/Mac)
  3. Run the following command:
    mvn tomcat:run
  4. You should see the following output:
    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'tomcat'.
    [INFO] artifact org.codehaus.mojo:tomcat-maven-plugin: checking for updates from central
    [INFO] artifact org.codehaus.mojo:tomcat-maven-plugin: checking for updates from snapshots
    [INFO] ------------------------------------------
    [INFO] Building spring-neo4j-tutorial Maven Webapp
    [INFO]    task-segment: [tomcat:run]
    [INFO] ------------------------------------------
    [INFO] Preparing tomcat:run
    [INFO] [apt:process {execution: default}]
    [INFO] [resources:resources {execution: default-resources}]
    [INFO] [tomcat:run {execution: default-cli}]
    [INFO] Running war on http://localhost:8080/spring-neo4j-tutorial
    Mar 9, 2012 7:19:07 AM org.apache.catalina.startup.Embedded start
    INFO: Starting tomcat server
    Mar 9, 2012 7:19:07 AM org.apache.catalina.core.StandardEngine start
    INFO: Starting Servlet Engine: Apache Tomcat/6.0.29
    Mar 9, 2012 7:19:07 AM org.apache.catalina.core.ApplicationContext log
    INFO: Initializing Spring root WebApplicationContext
    Mar 9, 2012 7:19:11 AM org.apache.coyote.http11.Http11Protocol init
    INFO: Initializing Coyote HTTP/1.1 on http-8080
    Mar 9, 2012 7:19:11 AM org.apache.coyote.http11.Http11Protocol start
    INFO: Starting Coyote HTTP/1.1 on http-8080
    
  5. Note: If the project will not build due to missing repositories, please enable the repositories section in the pom.xml!

Access the Entry page

  1. Follow the steps with Building with Maven
  2. Open a browser
  3. Enter the following URL (8080 is the default port for Tomcat):
    http://localhost:8080/spring-neo4j-tutorial/

Import the project in Eclipse

  1. Ensure Maven is installed
  2. Open a command window (Windows) or a terminal (Linux/Mac)
  3. Run the following command:
    mvn eclipse:eclipse -Dwtpversion=2.0
  4. You should see the following output:
    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'eclipse'.
    [INFO] org.apache.maven.plugins: checking for updates from central
    [INFO] org.apache.maven.plugins: checking for updates from snapshots
    [INFO] org.codehaus.mojo: checking for updates from central
    [INFO] org.codehaus.mojo: checking for updates from snapshots
    [INFO] artifact org.apache.maven.plugins:maven-eclipse-plugin: checking for updates from central
    [INFO] artifact org.apache.maven.plugins:maven-eclipse-plugin: checking for updates from snapshots
    [INFO] -----------------------------------------
    [INFO] Building spring-neo4j-tutorial Maven Webapp
    [INFO]    task-segment: [eclipse:eclipse]
    [INFO] -----------------------------------------
    [INFO] Preparing eclipse:eclipse
    [INFO] No goals needed for project - skipping
    [INFO] [eclipse:eclipse {execution: default-cli}]
    [INFO] Adding support for WTP version 2.0.
    [INFO] -----------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] -----------------------------------------
    
    This command will add the following files to your project:
    .classpath
    .project
    .settings
    target
    You may have to enable "show hidden files" in your file explorer to view them
  5. Open Eclipse and import the project

Conclusion

That's it! We've have successfully completed our Spring MVC 3.1 web application. We've learned how to integrate Neo4j using the Spring Data Neo4j framework, and studied how to map our domain objects to the Neo4j graph. We've only explored the basics of Neo4j, but I hope that this simple tutorial will provide a good start.

I hope you've enjoyed this tutorial. Don't forget to check my other tutorials at the Tutorials section.

Revision History


Revision Date Description
1 March 8 2012 Uploaded tutorial
2 March 9 2012 Uploaded source code to Github

StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.1 - Implement CRUD with Spring Data Neo4j (Part 5) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Saturday, February 19, 2011

Spring Data - MongoDB Tutorial (1.0.0.M1)

Introduction

In this tutorial we will refactor an existing Spring MVC 3 - MongoDB application (see here) to use the newly released Spring Data Document 1.0.0.M1 for MongoDB. Our purpose here is to realize how Spring Data simplifies integration development with MongoDB. To appreciate Spring Data, it is advisable to know how to perform basic CRUD functions with native MongoDB support. And that's the reason why we'll be refactoring an existing Spring MVC 3 - MongoDB application.

Note: An updated version of this tutorial is now accessible at Spring MVC 3.1 - Implement CRUD with Spring Data MongoDB (Part 1)

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.

Source: http://www.mongodb.org/

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.

Source: Spring Datastore Document - Reference Documentation
In a nutshell MongoDB uses JSON instead of SQL There's no static schema to create. All schemas are dynamic, meaning you create them on-the-fly. You can try a real-time online shell for MongoDB at http://try.mongodb.org/. Visit the official MongoDB site for a thorough discussion.

Prerequisites
In order to complete this tutorial, you will be required to install a copy of MongoDB. If you don't have one yet, grab a copy now by visiting http://www.mongodb.org/display/DOCS/Quickstart. Installation is really easy.

Development

Our application is a simple CRUD system for managing a list of Persons. 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.

The Domain Layer

Our application contains a single domain object named Person. It consists the following properties:
pid
firstName
lastName
money
Here's the class declaration:

Person.java
package org.krams.tutorial.domain;

import java.io.Serializable;

/**
 * A simple POJO representing a Person
 * 
 * @author Krams at {@link http://krams915@blogspot.com}
 */
public class Person implements Serializable {

 private static final long serialVersionUID = -5527566248002296042L;
 
 private String pid;
 private String firstName;
 private String lastName;
 private Double money;

 public String getPid() {
  return pid;
 }

 public void setPid(String pid) {
  this.pid = pid;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public Double getMoney() {
  return money;
 }

 public void setMoney(Double money) {
  this.money = money;
 }
}
Warning!
In the original tutorial we used id to signify the primary identity of the Person. But in this tutorial we used pid instead. This is because when we use id with Spring Data, it messes up the id property by merging it with the built-in _id property of MongoDB.

For now, if you plan to use Spring Data, don't use id as your id field. Choose a different name instead. I have filed already an inquiry about this behavior in the Spring forums. See it here.

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.

Source: Spring Datastore Document - Reference Documentation

Here's the class declaration:

PersonService.java
package org.krams.tutorial.service;

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

import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.krams.tutorial.domain.Person;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.document.mongodb.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static org.springframework.data.document.mongodb.query.Criteria.where;

/**
 * Service for processing {@link Person} objects.
 * Uses Spring's {@link MongoTemplate} to perform CRUD operations.
 * <p>
 * For a complete reference to MongoDB
 * see http://www.mongodb.org/
 * <p>
 * For a complete reference to Spring Data MongoDB 
 * see http://www.springsource.org/spring-data
 * 
 * @author Krams at {@link http://krams915@blogspot.com}
 */
@Service("personService")
@Transactional
public class PersonService {

 protected static Logger logger = Logger.getLogger("service");
 
 @Resource(name="mongoTemplate")
 private MongoTemplate mongoTemplate;
 
 /**
  * Retrieves all persons
  */
 public List<Person> getAll() {
  logger.debug("Retrieving all persons");
 
  // Find an entry where pid property exists
        Query query = new Query(where("pid").exists(true));
        // Execute the query and find all matching entries
        List<Person> persons = mongoTemplate.find(query, Person.class);
        
  return persons;
 }
 
 /**
  * Retrieves a single person
  */
 public Person get( String id ) {
  logger.debug("Retrieving an existing person");
  
  // Find an entry where pid matches the id
        Query query = new Query(where("pid").is(id));
        // Execute the query and find one matching entry
        Person person = mongoTemplate.findOne("mycollection", query, Person.class);
     
  return person;
 }
 
 /**
  * Adds a new person
  */
 public Boolean add(Person person) {
  logger.debug("Adding a new user");
  
  try {
   
   // Set a new value to the pid property first since it's blank
   person.setPid(UUID.randomUUID().toString());
   // Insert to db
      mongoTemplate.insert("mycollection", person);

   return true;
   
  } catch (Exception e) {
   logger.error("An error has occurred while trying to add new user", e);
   return false;
  }
 }
 
 /**
  * Deletes an existing person
  */
 public Boolean delete(String id) {
  logger.debug("Deleting existing person");
  
  try {
   
   // Find an entry where pid matches the id
         Query query = new Query(where("pid").is(id));
         // Run the query and delete the entry
         mongoTemplate.remove(query);
         
   return true;
   
  } catch (Exception e) {
   logger.error("An error has occurred while trying to delete new user", e);
   return false;
  }
 }
 
 /**
  * Edits an existing person
  */
 public Boolean edit(Person person) {
  logger.debug("Editing existing person");
  
  try {
   
   // Find an entry where pid matches the id
         Query query = new Query(where("pid").is(person.getPid()));
         
   // Declare an Update object. 
         // This matches the update modifiers available in MongoDB
   Update update = new Update();
         
         update.set("firstName", person.getFirstName());
         mongoTemplate.updateMulti(query, update);
         
         update.set("lastName", person.getLastName());
         mongoTemplate.updateMulti(query, update);
         
         update.set("money", person.getMoney());
         mongoTemplate.updateMulti(query, update);
         
   return true;
   
  } catch (Exception e) {
   logger.error("An error has occurred while trying to edit existing user", e);
   return false;
  }
  
 }
}
The code should should be self-explanatory. Notice how Spring Data has reduced the amount of code. To appreciate this difference, let's do a comparison between using the traditional MongoDB and using Spring Data.

Retrieving all entries

old implementation
public List<person> getAll() {
  logger.debug("Retrieving all persons");
   
  // Retrieve collection
  DBCollection coll = MongoDBFactory.getCollection("mydb","mycollection");
  // Retrieve cursor for iterating records
     DBCursor cur = coll.find();
     // Create new list
  List<person> items = new ArrayList<person>();
  // Iterate cursor
        while(cur.hasNext()) {
         // Map DBOject to Person
         DBObject dbObject = cur.next();
         Person person = new Person();
          
         person.setId(dbObject.get("id").toString());
         person.setFirstName(dbObject.get("firstName").toString());
         person.setLastName(dbObject.get("lastName").toString());
         person.setMoney(Double.valueOf(dbObject.get("money").toString()));
 
         // Add to new list
         items.add(person);
        }
         
        // Return list
  return items;
 }

new implementation
public List<Person> getAll() {
  logger.debug("Retrieving all persons");
 
        Query query = new Query(where("pid").exists(true));
        List<Person> persons = mongoTemplate.find(query, Person.class);
        
  return persons;
 }

Retrieving a single entry

old implementation
public Person get( String id ) {
  logger.debug("Retrieving an existing person");
   
  // Retrieve collection
  DBCollection coll = MongoDBFactory.getCollection("mydb","mycollection");
  // Create a new object
  DBObject doc = new BasicDBObject();
  // Put id to search
        doc.put("id", id);
         
        // Find and return the person with the given id
        DBObject dbObject = coll.findOne(doc);
         
        // Map DBOject to Person
     Person person = new Person();
     person.setId(dbObject.get("id").toString());
     person.setFirstName(dbObject.get("firstName").toString());
     person.setLastName(dbObject.get("lastName").toString());
     person.setMoney(Double.valueOf(dbObject.get("money").toString()));
      
        // Return person
  return person;
 }

new implementation
public Person get( String id ) {
  logger.debug("Retrieving an existing person");
  
  // Find an entry where pid matches the id
        Query query = new Query(where("pid").is(id));
        // Execute the query and find one matching entry
        Person person = mongoTemplate.findOne("mycollection", query, Person.class);
     
  return person;
 }

Adding a new entry

old implementation
public Boolean add(Person person) {
  logger.debug("Adding a new user");
   
  try {
   // Retrieve collection
   DBCollection coll = MongoDBFactory.getCollection("mydb","mycollection");
   // Create a new object
   BasicDBObject doc = new BasicDBObject();
   // Generate random id using UUID type 4
   // See http://en.wikipedia.org/wiki/Universally_unique_identifier
         doc.put("id", UUID.randomUUID().toString() ); 
         doc.put("firstName", person.getFirstName());
         doc.put("lastName", person.getLastName());
         doc.put("money", person.getMoney());
         // Save new person
         coll.insert(doc);
          
   return true;
    
  } catch (Exception e) {
   logger.error("An error has occurred while trying to add new user", e);
   return false;
  }
 }

new implementation
public Boolean add(Person person) {
  logger.debug("Adding a new user");
  
  try {
   
   // Set a new value to the pid property first since it's blank
   person.setPid(UUID.randomUUID().toString());
   // Insert to db
      mongoTemplate.insert("mycollection", person);

   return true;
   
  } catch (Exception e) {
   logger.error("An error has occurred while trying to add new user", e);
   return false;
  }
 }

Deleting an entry

old implementation
public Boolean delete(String id) {
  logger.debug("Deleting existing person");
   
  try {
   // Retrieve person to delete
   BasicDBObject item = (BasicDBObject) getDBObject( id );
   // Retrieve collection
   DBCollection coll = MongoDBFactory.getCollection("mydb","mycollection");
   // Delete retrieved person
         coll.remove(item);
          
   return true;
    
  } catch (Exception e) {
   logger.error("An error has occurred while trying to delete new user", e);
   return false;
  }
 }

new implementation
public Boolean delete(String id) {
  logger.debug("Deleting existing person");
  
  try {
   
   // Find an entry where pid matches the id
         Query query = new Query(where("pid").is(id));
         // Run the query and delete the entry
         mongoTemplate.remove(query);
         
   return true;
   
  } catch (Exception e) {
   logger.error("An error has occurred while trying to delete new user", e);
   return false;
  }
 }

Updating an entry

old implementation
public Boolean edit(Person person) {
  logger.debug("Editing existing person");
   
  try {
   // Retrieve person to edit
   BasicDBObject existing = (BasicDBObject) getDBObject( person.getId() );
    
   DBCollection coll = MongoDBFactory.getCollection("mydb","mycollection");
    
   // Create new object
   BasicDBObject edited = new BasicDBObject();
   // Assign existing details
   edited.put("id", person.getId()); 
   edited.put("firstName", person.getFirstName());
   edited.put("lastName", person.getLastName());
   edited.put("money", person.getMoney());
   // Update existing person
         coll.update(existing, edited);
          
   return true;
    
  } catch (Exception e) {
   logger.error("An error has occurred while trying to edit existing user", e);
   return false;
  }
   
 }

new implementation
public Boolean edit(Person person) {
  logger.debug("Editing existing person");
  
  try {
   
   // Find an entry where pid matches the id
         Query query = new Query(where("pid").is(person.getPid()));
         
   // Declare an Update object. 
         // This matches the update modifiers available in MongoDB
   Update update = new Update();
         
         update.set("firstName", person.getFirstName());
         mongoTemplate.updateMulti(query, update);
         
         update.set("lastName", person.getLastName());
         mongoTemplate.updateMulti(query, update);
         
         update.set("money", person.getMoney());
         mongoTemplate.updateMulti(query, update);
         
   return true;
   
  } catch (Exception e) {
   logger.error("An error has occurred while trying to edit existing user", e);
   return false;
  }
  
 }

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 satifies 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"/>
 
 <!-- Offers convenience methods and automatic mapping between MongoDB JSON documents and your domain classes. -->
   <bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
     <constructor-arg ref="mongo"/>
     <constructor-arg value="mydb"/>
     <constructor-arg value="mycollection"/>
   </bean>
   
   <bean id="initService" class="org.krams.tutorial.service.InitService" init-method="init"></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 (mydb), and a collection (mycollection):
<bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
     <constructor-arg ref="mongo"/>
     <constructor-arg value="mydb"/>
     <constructor-arg value="mycollection"/>
   </bean>

Lastly, we declared an initService
<bean id="initService" class="org.krams.tutorial.service.InitService" init-method="init"></bean>
The purpose of the initService is to prepopulate our MongoDB with sample data.

Here's the class declaration:

InitService.java
package org.krams.tutorial.service;

import java.util.UUID;

import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.krams.tutorial.domain.Person;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.transaction.annotation.Transactional;

/**
 * Service for initializing MongoDB with sample data
 * <p>
 * For a complete reference to MongoDB
 * see http://www.mongodb.org/
 * <p>
 * For transactions, see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
 * 
 * @author Krams at {@link http://krams915@blogspot.com}
 */
@Transactional
public class InitService {

 protected static Logger logger = Logger.getLogger("service");
 
 @Resource(name="mongoTemplate")
 private MongoTemplate mongoTemplate;

 private void init() {
  // Populate our MongoDB database
  logger.debug("Init MongoDB users");
  
  // Drop existing collection
  mongoTemplate.dropCollection("mycollection");
  
  // Create new object
  Person p = new Person ();
  p.setPid(UUID.randomUUID().toString());
  p.setFirstName("John");
  p.setLastName("Smith");
  p.setMoney(1000.0);
  
  // Insert to db
     mongoTemplate.insert("mycollection", p);

     // Create new object
  p = new Person ();
  p.setPid(UUID.randomUUID().toString());
  p.setFirstName("Jane");
  p.setLastName("Adams");
  p.setMoney(2000.0);
  
  // Insert to db
     mongoTemplate.insert("mycollection", p);
        
     // Create new object
  p = new Person ();
  p.setPid(UUID.randomUUID().toString());
  p.setFirstName("Jeff");
  p.setLastName("Mayer");
  p.setMoney(3000.0);
  
  // Insert to db
     mongoTemplate.insert("mycollection", p);
 }
}

The Controller Layer

After creating the domain and service classes, we need to declare a controller that will handle the web requests.

MainController.java
package org.krams.tutorial.controller;

import java.util.List;
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.krams.tutorial.domain.Person;
import org.krams.tutorial.service.PersonService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.bind.annotation.RequestParam;


/**
 * Handles and retrieves person request
 * 
 * @author Krams at {@link http://krams915@blogspot.com}
 */
@Controller
@RequestMapping("/main")
public class MainController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="personService")
 private PersonService personService;
 
 /**
  * Handles and retrieves all persons and show it in a JSP page
  * 
  * @return the name of the JSP page
  */
    @RequestMapping(value = "/persons", method = RequestMethod.GET)
    public String getPersons(Model model) {
     
     logger.debug("Received request to show all persons");
     
     // Retrieve all persons by delegating the call to PersonService
     List persons = personService.getAll();
     
     // Attach persons to the Model
     model.addAttribute("persons", persons);
     
     // This will resolve to /WEB-INF/jsp/personspage.jsp
     return "personspage";
 }
    
    /**
     * Retrieves the add page
     * 
     * @return the name of the JSP page
     */
    @RequestMapping(value = "/persons/add", method = RequestMethod.GET)
    public String getAdd(Model model) {
     logger.debug("Received request to show add page");
    
     // Create new Person and add to model
     // This is the formBackingOBject
     model.addAttribute("personAttribute", new Person());

     // This will resolve to /WEB-INF/jsp/addpage.jsp
     return "addpage";
 }
 
    /**
     * Adds a new person by delegating the processing to PersonService.
     * Displays a confirmation JSP page
     * 
     * @return  the name of the JSP page
     */
    @RequestMapping(value = "/persons/add", method = RequestMethod.POST)
    public String add(@ModelAttribute("personAttribute") Person person) {
  logger.debug("Received request to add new person");
  
     // The "personAttribute" model has been passed to the controller from the JSP
     // We use the name "personAttribute" because the JSP uses that name
  
  // Call PersonService to do the actual adding
  personService.add(person);

     // This will resolve to /WEB-INF/jsp/addedpage.jsp
  return "addedpage";
 }
    
    /**
     * Deletes an existing person by delegating the processing to PersonService.
     * Displays a confirmation JSP page
     * 
     * @return  the name of the JSP page
     */
    @RequestMapping(value = "/persons/delete", method = RequestMethod.GET)
    public String delete(@RequestParam(value="pid", required=true) String id, 
              Model model) {
   
  logger.debug("Received request to delete existing person");
  
  // Call PersonService to do the actual deleting
  personService.delete(id);
  
  // Add id reference to Model
  model.addAttribute("pid", id);
     
     // This will resolve to /WEB-INF/jsp/deletedpage.jsp
  return "deletedpage";
 }
    
    /**
     * Retrieves the edit page
     * 
     * @return the name of the JSP page
     */
    @RequestMapping(value = "/persons/edit", method = RequestMethod.GET)
    public String getEdit(@RequestParam(value="pid", required=true) String id,  
              Model model) {
     logger.debug("Received request to show edit page");
    
     // Retrieve existing Person and add to model
     // This is the formBackingOBject
     model.addAttribute("personAttribute", personService.get(id));
     
     // This will resolve to /WEB-INF/jsp/editpage.jsp
     return "editpage";
 }
    
    /**
     * Edits an existing person by delegating the processing to PersonService.
     * Displays a confirmation JSP page
     * 
     * @return  the name of the JSP page
     */
    @RequestMapping(value = "/persons/edit", method = RequestMethod.POST)
    public String saveEdit(@ModelAttribute("personAttribute") Person person, 
                 @RequestParam(value="pid", required=true) String id, 
                Model model) {
     logger.debug("Received request to update person");
    
     // The "personAttribute" model has been passed to the controller from the JSP
     // We use the name "personAttribute" because the JSP uses that name
     
     // We manually assign the id because we disabled it in the JSP page
     // When a field is disabled it will not be included in the ModelAttribute
     person.setPid(id);
     
     // Delegate to PersonService for editing
     personService.edit(person);
     
     // Add id reference to Model
  model.addAttribute("pid", id);
  
     // This will resolve to /WEB-INF/jsp/editedpage.jsp
  return "editedpage";
 }
    
}
Our controller is a simple class that delegates actual processing to PersonService. When the service is done processing, the controller forwards the result to a JSP view.

Other Configurations and Files

To make the tutorial manageable, I've decided not to post the following configuration files in this tutorial:
web.xml
spring-servlet.xml
applicationContext.xml
These files are standard Spring MVC related configuration files. You can find them in the downloadable application at the end of this tutorial.

I have also left out the JSP declarations. You can find a description of them in the following tutorial: Spring MVC 3: Using a Document-Oriented Database - MongoDB

Run the Application

To run the application, open your browser and enter the following URL:
http://localhost:8080/spring-data-mongodb/krams/main/persons
You should see the following CRUD view:


Conclusion

That's it. We have successfully refactored our existing Spring MVC 3 - MongoDB application to use the newly released Spring Data Document 1.0 for MongoDB. We've compared side-by-side between the native MongoDB development and with the new Spring Data framework. We have seen how Spring Data has simplified our development further.

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-mvc-mongodb/

You can download the project as a Maven build. Look for the spring-data-mongodb.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring Data - MongoDB Tutorial (1.0.0.M1) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share