Integrating MongoDB & Spring

Further to my previous post with a deck walking through MongoDB & Spring integration, I also recently wrote it up as a technical article for work (my day job), and I thought I would share the detailed article here for you as well..


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).
For that reason, this article will look at a simple web app that allows users to create Resumes – Similar to an essay, resume storage is naturally suited to the Document based approach rather than a Relational approach.


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.
The source code for the application is all available from my GitHub Account (see links on the right), and if you want to follow the code below are the required pre-requisites:
  • Install MongoDB – this is a straight forward process and details can be found here: http://www.mongodb.org
  • Install Maven – the example project uses Maven to manage it dependencies, details can be found here: http://maven.apache.org/
  • Install STS Eclipse build and install the CloudFoundry extension (Optional) – If you want to follow the steps to deploy the application to the cloud then this is required
  • Sign up for a CloudFoundry account (www.cloudfoundry.com) (Optional) – Currently in Beta so its recommended that you sign up for the account as soon as possible as requests can take time to come through






Project Dependencies

Spring currently has a project underway to integrate core Spring functionality with various non–relational data technologies, including MongoDB, called “Spring Data”. We will use this library to facilitate our integration, so we will need to include the following maven dependency in our project pom:

              <dependency>
                     <groupId>org.springframework.data</groupId>
                     <artifactId>spring-data-mongodb</artifactId>
                     <version>1.0.0.M3</version>
              </dependency>


We also need to include the MongoDB Java driver:

              <dependency>
                     <groupId>org.mongodb</groupId>
                     <artifactId>mongo-java-driver</artifactId>
                     <version>2.6.5</version>
              </dependency>


And finally, to deploy on to CloudFoundry’s hosted service we need their runtime library:

              <dependency>
                     <groupId>org.cloudfoundry</groupId>
                     <artifactId>cloudfoundry-runtime</artifactId>
                     <version>0.7.1</version>
              </dependency>




Building the Domain Model

If you are familiar with using popular JPA frameworks in your Spring web apps (Hibernate/Eclipselink, etc) then this part should look familiar – Like any application we will need to create some objects to model our underlying data (the “M” in the MVC). For our simple application we will have three basic objects:



As you can see, we have three simple objects in our model, Resume, ResumePage, Section (we will just use these objects to split our document down in to several sections/chapters). 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
Resume.java:

@Document
public class Resume {
      
       @Id
       private String id;
      
       private List<ResumePage> pages = new ArrayList<ResumePage>();
...


That’s it! We use the @Document and @Id annotation to indicate any object that we want to treat as a Document, and the ID. We only need to add these annotations on our Resume object, as in this case the Resume is our document, we do not want to store our pages or sections as individual documents in themselves.



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. In our example app, we only want CRUD functionality so we just create a basic Interface that extends MongoRepository:

@Transactional
public interface IResumeRepository extends MongoRepository<Resume, String>{}

Next, in our Service layer we will auto-wire our new Interface in to our service classes (this is normal Spring stuff, using the @Autowired annotation we are telling Spring to handle the dependency injection for this interface):

@Repository("profileService")
@Transactional
public class ProfileService{

       @Autowired
       private IResumeRepository resumeRepo;


This makes the CRUD functionality available in our Service class to perform common functions, such as loadResume (in this case, we are using the resume owner’s name as the ID for the documents):

public Resume get(String userName) {
       return resumeRepo.findOne(userName.toLowerCase());
}


Or createResume:

       public Boolean createResume(Resume r) {
              try {
                     // Insert to db
                     resumeRepo.save(r);
                     return true;

              } catch (Exception e) {
                     //log exceptions here!
                     return false;
              }
       }


By simply extending the MongoRepository Interface and then auto-wiring it into our Service classes, we have provided basic CRUD functionality through the MongoRepository API as well as ensuring that our service class is not tightly coupled with the DAO implementation. However, often applications will need custom queries beyond basic CRUD. This can also be simply achieved using the MongoTemplate class provided, for example:

Query query = new Query(where("id").is(“rob”));
Resume r = mongoTemplate.findOne("mycollection", query, Resume.class);

The above query will retrieve the Resume belonging to “Rob”. The Query object provides rich API for accessing and updating documents in your data store.



Spring Configuration

The final step of our integration is to configure the Spring beans to handle the data source properties.
The Spring configuration resides in the Application Context XML file (in our case, it’s a file named “applicationContext.xml” and can be found in src/main/resources/META-INF/spring/.
This file is used to declare some of the Spring beans (although not all beans will be declared in here – some beans are declared using annotations, such as our Service classes), for our MongoDB integration we need to include the following configuration:

       <!-- Mongo Configuration -->
       <mongo:repositories base-package="com.tmm.nosql.mongodb.repo" />
      
       <bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
              <constructor-arg ref="mongoDbFactory" />
       </bean>
      
       <!-- local config -->
       <mongo:db-factory id="mongoDbFactory" dbname="resume_db" host="localhost" port="27017"/>


The first element of the config declares the name of the package that our Repositories are stored in (this is where the our DAO Repository lives) – we declare this so to enable the package to be scanned for relevant repository classes.
The second configuration element declares the Mongo Template bean (provided by the Spring Data library) and we declare that we want Spring to inject our MongoDbFactory bean using Constructor based Dependency Injection.
The final configuration option declares the MongoDbFactory that we will be injecting in to our MongoTemplate bean – here we need to define the database server and port name. The default port number is 27017, so unless you have altered from the standard MongoDB install and configuration then you should use these details.




Running the Application

Running Locally

To test the application, set up a Tomcat server within your Eclipse (IDE) and add the project to the server, start the server and navigate to http://localhost:8080/ and you should see a simple welcome page.

Running on CloudFoundry

Before running on CloudFoundry we need to make a very minor tweak to the configuration. Previously we had added the configuration option:

<mongo:db-factory id="mongoDbFactory" dbname="resume_db" host="localhost" port="27017"/>

As we are no longer running on localhost and want to bind these options to CloudFoundry’s services, we change this to:

<cloud:mongo-db-factory id="mongoDbFactory"/>

Simple enough again – we just declare our MongoDbFactory, and leave CloudFoundry to inject the required parameters.


The next step to deploying the application on CloudFoundry is to add a CloudFoundry server instance to your eclipse:
  • Open the “Servers” view in Eclipse (Window>Show View>Servers)
  • Right-click and select New>Server

  • Select VMWare > Cloud Foundry and then press the “Next” button
  • You will then have to enter your Cloud Foundry account information:



  • Complete your email and password information, leaving the URL as it is and select the Next button.
  • The final screen will give you the option to add the project to the server, add the server and press Finish



Now the Cloud Foundry server instance is setup within your IDE you need to configure the deployed application so you can bind it to the required services:
  • Double-click on your deployed project within the “Servers” view:


  • This will open the “Applications” page – you will notice in the bottom left had corner an empty box titled Services, 




  • Press the “Add Service” button to the top right of the Services panel:




  • The MongoDB service will now be listed in the Services box, now drag it across to the right to the “Application Services” section. The “Applications” panel should now appear something like this:



  • Press the “Start” button under the General tab, then watch the log as the application is started up on the CloudFoundry – there should be no errors reported
  • Now navigate to the URL you assigned to the application (this can be edited in the above panel under the “General” section


You now have a MongoDB powered NoSQL application running in the Cloud!

rob hinds

I'm on to the next one, on to the next one..

0 comments: