Overview

Spring Data – Part 3: MongoDB

No Comments

In this part of my blog series I’m going to show how easy it is to access a MongoDB datastore with Spring Data MongoDB.

MongoDB

MongoDB is a so called NoSQL datastore for document-oriented storage. A good place to start with MongoDB is the Developer Zone on the project’s homepage. After downloading and installing MongoDB we create a folder for data storage and start the server with

${MONGO_HOME}/bin/mkdir ../data
${MONGO_HOME}/bin/mongod --dbpath ../data --rest

and are welcomed by a web admin interface at http://localhost:28017/. To play around with MongoDB, use the interactive mongo shell:

C:\dev\bin\mongo\bin>mongo
MongoDB shell version: 2.0.2
connecting to: test
> show dbs
admin   (empty)
local   (empty)
test    0.078125GB
> show collections
foo
system.indexes
> db.foo.save({a:1, b:"bar"})
> db.foo.save({a:1, b:"bar"})
> db.foo.save({c:2, d:"doo"})
> db.foo.find()
{ "_id" : ObjectId("4f1e575efc25822cd8ff8cf2"), "a" : 1, "b" : "bar" }
{ "_id" : ObjectId("4f1e5766fc25822cd8ff8cf3"), "a" : 1, "b" : "bar" }
{ "_id" : ObjectId("4f1e5771fc25822cd8ff8cf4"), "c" : 2, "d" : "doo" }

We display the names of the databases, than the collections (a collection is a logical namespace) inside the default database test. After that, we persists three documents in JSON notation. Doing so we observe:

  1. each document has a unique id
  2. there may be more than one document holding the same attribute set in the same collection
  3. documents with different structures can be stored in the same collection

So a collection is really not the same thing as a table of a relational database. We also have no support for ACID transaction handling. Welcome to the cloud!

Spring Data MongoDB

Spring Data MongoDB works basically the same way as Spring Data JPA: you define your custom repository finders by writing only interface methods and Spring provides an implementation at runtime. The basic CRUD operation are supported without the need to write a single line of code.

Configuration

First of all we let Maven download the latest realeae version of Spring Data MongoDB:

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

Using the mongo namespace your Spring application context can be configured quite easy:

<!-- Connection to MongoDB server -->
<mongo:db-factory host="localhost" port="27017" dbname="test" />
<!-- MongoDB Template -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
  <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
 
<!-- Package w/ automagic repositories -->
<mongo:repositories base-package="mongodb"/>

The connection to our MongoDB server and the database to use are configured with the <mongo:db-factory .../> tag. For fine tuning of the connection (connection pooling, clustering etc.) use the elements <mongo:mongo> und <mongo:options/> instead. Then we define a template that refers our DB factory. Finally we have to configure the package holding our repository interfaces (same as with Spring Data JPA). By default the only MongoDBTemplate inside the application context is used. If there are more than one template, you can specify which one to use with <mongo:repositories mongo-template-ref="...">.

Example

Similar to the blog post on Spring Data JPA we like to persist some simple User objects:

@Document
public class User {
 
	@Id 
	private String id;
 
	@Indexed
	private String fullName;
 
	private Date lastLogin;
	...

The annotations are not required. But to define an index we have to use the @Indexed annotation. To begin with we use a very simple repository …

public interface UserRepository extends MongoRepository<User, String> {}

… to save our first documents:

public class MongoDBRepoTest {
 
  @Autowired UserRepository repo;
 
  @Before public void setUp() {
    repo.save(new User("root", "Superuser"));
    for ( int i = 0; i < 6; i++ ) {
      repo.save( new User( String.format("user%02d", i), "User " + i ) );
    }
  }

We use the mongo shell to check if our documents were persisted:

MongoDB shell version: 1.8.3
connecting to: test
> db.user.find()
{ "_id" : "user00", "_class" : "mongodb.User", "fullName" : "User 0", "lastLogin" : ISODate("2012-01-27T08:16:37.589Z") }
{ "_id" : "user01", "_class" : "mongodb.User", "fullName" : "User 1", "lastLogin" : ISODate("2012-01-27T08:16:37.589Z") }
{ "_id" : "user02", "_class" : "mongodb.User", "fullName" : "User 2", "lastLogin" : ISODate("2012-01-27T08:16:37.590Z") }
{ "_id" : "user03", "_class" : "mongodb.User", "fullName" : "User 3", "lastLogin" : ISODate("2012-01-27T08:16:37.590Z") }
{ "_id" : "user04", "_class" : "mongodb.User", "fullName" : "User 4", "lastLogin" : ISODate("2012-01-27T08:16:37.591Z") }
{ "_id" : "user05", "_class" : "mongodb.User", "fullName" : "User 5", "lastLogin" : ISODate("2012-01-27T08:16:37.591Z") }
{ "_id" : "root", "_class" : "mongodb.User", "fullName" : "Superuser", "lastLogin" : ISODate("2012-01-27T08:16:37.576Z") }
> db.user.count()
7
> db.user.getIndexes()
[
        {
                "name" : "_id_",
                "ns" : "test.user",
                "key" : {
                        "_id" : 1
                },
                "v" : 0
        },
        {
                "name" : "fullName",
                "ns" : "test.user",
                "dropDups" : false,
                "sparse" : false,
                "unique" : false,
                "key" : {
                        "fullName" : 1
                },
                "v" : 0
        }
]

You may have noticed that a collection named user was created on the fly. If you want a non-default collection name (the lowercase name of the Java class), use the document annotation: @Document(collection="..."). The full qualified class name is persisted with the _class attribute. There are two indexes now: the default index for the id attribute and the index generated from the class attribute fullName with the @Indexed annotation.

Now we write some more custom finders:

public interface UserRepository extends MongoRepository<User, String> {
   @Query("{ fullName: ?0 }")
   List<User> findByTheUsersFullName(String fullName);
 
   List<User> findByFullNameLike(String fullName, Sort sort);
}

With the @Query annotation you can define random queries in MongoDB syntax. The second query shows a finder that provides a search with regular expressions. When writing your first queries the comparison between MongoDB and SQL can be very helpful.

The complete source code of the example can be downloaded from Github.

MongoDBTemplate

Not all MongoDB features are exposed with the interface based repository approach. If you want to manage collections or use map/reduce, you have to use the API of the MongoDBTemplate.

Summary

After a short introduction to MongoDB we were able to persist the first object very fast using Spring Data MongoDB. After that, we wrote custom finders with just a few lines of code.

A Spring application using Spring Data MongoDB as a persistence layer can be deployed to a cloud platform like CloudFoundry. This blog post show how easy that can be done.

What happened before?

Part 1: Spring Data Commons
Part 2: Spring Data JPA

What’s next?

Expect upcoming blog posts on Spring Data Neo4j and Spring GemFire.

Comment

Your email address will not be published. Required fields are marked *