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 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!
2 comments: