31 July 2011

Getting started with MongoDB and Spring Data

Last month I finally found some time to play around with a NoSQL database. Getting hands on experience with a NoSQL database has been on my list for quite some time, but due to busy times at work I was unable to find the energy to get things going.

A little background information


Most of you have probably have heard the term NoSQL before. The term is used in situations where you do not have a traditional relation database for storing information. There are many different sorts of NoSQL databases. To make a small summary these are probably the most well-known:


The above types cover most of the differences, but for each type there are a lot of different implementations. For a better overview you might want to take a look at the NOSQL database website.

For my own experiment I chose to use MongoDB, since I had read a lot about it and it seemed quite easy to get started with.

MongoDB is as they describe it on their website:
A scalable, high-performance, open source, document-oriented database.
The document-oriented aspect was one of the reasons why I chose MongoDB to start with. It allows you to store rich content with data structures inside your datastore.

Getting started with MongoDB


To begin with, I looked at the Quick start page for Mac OS X and I recommend you to do that too (unless you use a different OS). It will get you going and within a couple of minutes you'll have MongoDB up and running on your local machine.

MongoDB stores it's data by default in a certain location. Of course you can configure that, so I started MongoDB with the --dbpath parameter. This parameter will allow you to specificy your own storage location. It will look something like this:

$ ./mongodb-xxxxxxx/bin/mongod --dbpath=/Users/jreijn/Development/temp/mongodb/

If you do that you eventually will get a message saying:


Mon Jul 18 22:19:58 [initandlisten] waiting for connections on port 27017
Mon Jul 18 22:19:58 [websvr] web admin interface listening on port 28017


At this point MongoDB is running and we can proceed to the next step: using Spring Data to interact with MongoDB.

Getting started with Spring Data

The primary goal of the Spring Data project is to make it easier for developers to work with (No)SQL databases. The Spring Data project already has support for a number of the above mentioned NoSQL type of databases.
Since we're now using MongoDB, there is a specific sub project that handles MongoDB interaction. To be able to use this in our project we first need to add a Maven dependency to our pom.xml.

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-mongodb</artifactId>
  <version>${spring.data.mongo.version}</version>
</dependency>

Looks easy right? Just one single Maven dependency. Of course in the end the spring-data-mongodb artifact depends on other artifacts which it will bring into your project. In this post I used version 1.0.2.RELEASE.  Now on to some Java code!

For my first experiment I used a simple Person domain object that I'm going to query and persist inside the database. The Person class is quite simple and looks as follows.

package com.jeroenreijn.mongodb.example.domain;

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

/**
 * A simple POJO representing a Person
 *
 */
@Document
public class Person {

    @Id
    private String personId;

    private String name;
    private String homeTown;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getPersonId() {
        return personId;
    }

    public void setPersonId(final String personId) {
        this.personId = personId;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(final int age) {
        this.age = age;
    }

    public String getHomeTown() {
        return homeTown;
    }

    public void setHomeTown(final String homeTown) {
        this.homeTown = homeTown;
    }

    @Override
    public String toString() {
        return "Person [id=" + personId + ", name=" + name + ", age=" + age + ", home town=" + homeTown + "]";
    }

}

Now if you look at the class more closely you will see some Spring Data specific annotations like @Id and @Document . The @Document annotation identifies a domain object that is going to be persisted to MongoDB. Now that we have a persistable domain object we can move on to the real interaction.

For easy connectivity with MongoDB we can make use of Spring Data's MongoTemplate class. Here is a simple PersonRepository object that handles all 'Person' related interaction with MongoDB by means of the MongoTemplate.

package com.jeroenreijn.mongodb.example;

import java.util.Iterator;
import java.util.List;

import com.jeroenreijn.mongodb.example.domain.Person;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Repository;

/**
 * Repository for {@link Person}s
 *
 */
@Repository
public class PersonRepository {

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

    @Autowired
    MongoTemplate mongoTemplate;

    public void logAllPersons() {
        List<Person> results = mongoTemplate.findAll(Person.class);
        logger.info("Total amount of persons: {}", results.size());
        logger.info("Results: {}", results);
    }

    public void insertPersonWithNameJohnAndRandomAge() {
        //get random age between 1 and 100
        double age = Math.ceil(Math.random() * 100);

        Person p = new Person("John", (int) age);

        mongoTemplate.insert(p);
    }

    /**
     * Create a {@link Person} collection if the collection does not already exists
     */
    public void createPersonCollection() {
        if (!mongoTemplate.collectionExists(Person.class)) {
            mongoTemplate.createCollection(Person.class);
        }
    }

    /**
     * Drops the {@link Person} collection if the collection does already exists
     */
    public void dropPersonCollection() {
        if (mongoTemplate.collectionExists(Person.class)) {
            mongoTemplate.dropCollection(Person.class);
        }
    }
}


If you look at the above code you will see the MongoTemplate in action. There is quite a long list of method calls which you can use for inserting, querying and so on. The MongoTemplate in this case is @Autowired from the Spring configuration, so let's have a look at the configuration.

<?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"
       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">

  <!-- Activate annotation configured components -->
  <context:annotation-config/>

  <!-- Scan components for annotations within the configured package -->
  <context:component-scan base-package="com.jeroenreijn.mongodb.example">
    <context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/>
  </context:component-scan>

  <!-- Define the MongoTemplate which handles connectivity with MongoDB -->
  <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg name="mongo" ref="mongo"/>
    <constructor-arg name="databaseName" value="demo"/>
  </bean>

  <!-- Factory bean that creates the Mongo instance -->
  <bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean">
    <property name="host" value="localhost"/>
  </bean>

  <!-- Use this post processor to translate any MongoExceptions thrown in @Repository annotated classes -->
  <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

</beans>

The MongoTemplate is configured with a reference to a MongoDBFactoryBean (which handles the actual database connectivity) and is setup with a database name used for this example.

Now that we have all components in place, let's get something in and out of MongoDB.

package com.jeroenreijn.mongodb.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Small MongoDB application that uses spring data to interact with MongoDB.
 * 
 */
public class MongoDBApp {

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

  public static void main( String[] args ) {
    logger.info("Bootstrapping MongoDemo application");

    ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml");

    PersonRepository personRepository = context.getBean(PersonRepository.class);

    // cleanup person collection before insertion
    personRepository.dropPersonCollection();

    //create person collection
    personRepository.createPersonCollection();

    for(int i=0; i<20; i++) {
      personRepository.insertPersonWithNameJohnAndRandomAge();
    }

    personRepository.logAllPersons();
    logger.info("Finished MongoDemo application");
  }
}

All this application does for now is setup a connection with MongoDB, insert 20 persons (documents),  fetch them all and write the information to the log. As a first experiment this was quite fun to do.

Conclusion

As you can see with Spring Data it's quite easy to get some basic functionality within only a couple of minutes. All the sources mentioned above and a working project can be found on GitHub. It was a fun first experiment and I already started working on a bit more advanced project, which combines Spring Data, MongoDB, HTML5 and CSS3. It will be on GitHub shortly together with another blog post here so be sure to come back.

19 comments:

  1. I was just about to try this very same thing, so thanks!

    ReplyDelete
  2. Awesome! I think both technologies are really interesting. I hope this post gave you a quick starter.

    ReplyDelete
  3. How to skip the value in domain objects without changing the domain object class or using @Transient..

    ReplyDelete
  4. @Rameez:

    I do not fully understand what you want to achieve? If you have a domain object you want to skip the value only once of all the time?

    ReplyDelete
  5. what if i want to connect with a mongo db with auth mode? I mean with user and pass.

    ReplyDelete
  6. @KCOtzen: You can provide an extra constructor argument to the MongoTemplate that contains the user credentials. See http://support.cloudfoundry.com/entries/20018322-alert-mongo-bindings-have-changed for a small example at the bottom of the topic.

    ReplyDelete
  7. will try this.. mongo db looks impressive..

    ReplyDelete
  8. Hello,

    First I would like to thank Jeroen for publishing this. I found it helpful.

    For future readers, I noticed the packages have changed around a bit. Classes in package org.springframework.data.document.mongodb have moved to org.springframework.data.mongodb.core.

    -john

    ReplyDelete
    Replies
    1. Hi John,

      Thank you for the feedback! I had already updated the sample on Github some time ago. I've now also updated the article!

      Jeroen

      Delete
  9. this was really helpful.. i am able to use your code as 'seed' code to get all the integration going...!! this is simply great..!!

    Thanks again..

    ReplyDelete
  10. Using your 'seed' project, i was able to get things going:

    I was following up more on how to create Repository Interfaces mentioned in ,
    http://static.springsource.org/spring-data/data-mongo/docs/current/reference/html/, section 6, as it looks like I want to delegate the work of creating query methods to spring and only define my custom query methods with @Query annotations in the Repository interface. This way, i can delegate the responsibility of creating methods for custom queries to spring.

    In your example, you have used Repository class and annotated it with @Repository and left it up to spring's auto-scanning to identify the repo objects.

    Have you used the way mentioned in the link that i have shared ? What do you think will be the best way to proceed?

    I appreciate your help on this.

    Thanks
    Sarthak

    ReplyDelete
  11. Do we have to use maven? I just want to create console application and I want to send some data to MongoDB.
    Can you do configuration not only xml but also annotations.
    Regards

    ReplyDelete
  12. Can you add source codes of this example?

    ReplyDelete
    Replies
    1. The sources can be found here: https://github.com/jreijn/spring-mongo-demo

      Delete
  13. This sample is really very help full for me, so thanks!!!!!!

    ReplyDelete
  14. Can't filter? say you want to filter the results? is that possible? like you want all persons near point(1,1) with age of 35.....i can't figure out how to do this in Spring Data/MongoDB GeoNear ?

    ReplyDelete
    Replies
    1. Hi Jeryl,

      it seems to me that you can do this quite easily with geospatial support in spring data. See http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/mongo.core.html#mongo.geospatial for more info.

      Jeroen

      Delete
  15. Very clear and easy to understand. Thanks Jeroen

    ReplyDelete