Enterprise Java

MongoDB with Spring Data project

All of us are observing the explosion of NoSql solutions these days. I get used to RDBMS but those are not a solution for all kind of challenges you might have. In my recent experience I got a chance to work with MongoDB – document database. In this post I intent to cover some basics (and some advanced features in next post) of using MongoDB together with Spring Data project. Before we start, small disclaimer: at the moment Spring Data is still in milestone phase so some classes / interfaces may change.

Before we start, please download and run MongoDB for your operating system. It’s very simple so I won’t spend time on this and let’s start with simple POM file for our project:

    4.0.0

    mongodb
    com.example.spring
    0.0.1-SNAPSHOT
    jar

    
        UTF-8
        3.0.5.RELEASE
    

    
        
            org.springframework.data
            spring-data-mongodb
            1.0.0.M3
        

        
            log4j
            log4j
            1.2.16
        

        
            org.mongodb
            mongo-java-driver
            2.5.3
        

        
            org.springframework
            spring-core
            ${spring.version}
        

        
            org.springframework
            spring-context
            ${spring.version}
        
    

    
        
            springsource-milestone
            Spring Framework Milestone Repository
            http://maven.springframework.org/milestone
        
    

There are two key dependencies here:

MongoDB java driver
Spring Data for MongoDB

There are few ways to define MongoDB inside your Spring application context. Let me show a bit verbose but more flexible one:

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/data/mongo
        http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    
    
  
    
 
    
        <constructor-arg index="0" ref="mongo" />
        <constructor-arg index="1" value="elements-db"/>
    

        
      

    
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
        <constructor-arg name="mappingContext" ref="mappingContext" />         
     

    
        <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
        <constructor-arg name="mongoConverter" ref="converter" />        
        <property name="writeResultChecking" value="EXCEPTION" />
        <property name="writeConcern" value="NORMAL"/>
    
 
    
        
    

The role of each bean here:

  • mongo defines connection to MongoDB database (we rely on default settings, port 27027)
  • converter is used to convert Java classes to/from MongoDB’s DBObject (== JSON)
  • mongoTemplate exposes operations we can do over MongoDB

So, we are ready to go!

Here are few code snippets to start with:

package com.example.mongodb;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoOperations;
import org.springframework.data.document.mongodb.query.Index;
import org.springframework.data.document.mongodb.query.Index.Duplicates;
import org.springframework.data.document.mongodb.query.Order;
import org.springframework.stereotype.Service;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.MongoException;

@Service
public class MongoService  {
    @Autowired private MongoOperations template;

    public void createCollection( final String name ) {
        template.createCollection( name  );
    }

    public void dropCollection( final String name ) {
        template.dropCollection( name  );
    }

    public void insert( final Object object, final String collection ) {
        template.insert( object, collection );
    }
   
    public void createIndex( final String name, final String collection ) {
        template.ensureIndex( 
            new Index()
                .on( name, Order.DESCENDING )
                .unique( Duplicates.DROP ), 
            collection  
        );
    }
 
    // Remove / save / ... operations here
}

That’s it with basics. Next post will cover advanced features: using bulk inserts, update or insert operation and executing MongoDB commands. :)

After the discussion about MongoDB and Spring Data projects, i would like to show some advanced features (which could be available in next Spring Data milestone or release as part of core functionality).

First of all, let us extend our MongoService with a method that counts documents in collection which match specific query.

package com.example.mongodb;
import java.util.Arrays;
import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoOperations;
import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.query.Criteria;
import org.springframework.data.document.mongodb.query.Index;
import org.springframework.data.document.mongodb.query.Index.Duplicates;
import org.springframework.data.document.mongodb.query.Order;
import org.springframework.data.document.mongodb.query.Query;

import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.MongoException;

@Service
public class MongoService {
    public long countDocuments( final String collection, final Query query ) {  
        return template.executeCommand( 
            "{ " +
                "\"count\" : \"" + collection + "\"," +
                "\"query\" : " + query.getQueryObject().toString() + 
            " }"  ).getLong( "n" );
    }
}

The approach for this particular functionality is to call native MongoDB command count passing the query as a parameter. The returning structure contains number of documents in n property.

Or, in more code-friendly way:

import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.CollectionCallback;

import com.mongodb.DBCollection;
import com.mongodb.MongoException;

public long countDocuments( final String collection, final Query query ) {  
    return template.execute( collection,
        new CollectionCallback< Long >() {
            @Override
            public Long doInCollection( DBCollection collection ) 
                    throws MongoException, DataAccessException {
                return collection.count( q.getQueryObject() ) );
            }
        }
    );
}

Next useful feature is bulk inserts. Please note, that in current version of MongoDB 1.8.1, when there is a duplicate inside the collection of inserting documents, bulk insert stops on first duplicate and returns so all other documents won’t be inserted. Be aware of such behavior. Before moving to code snippet, let me introduce simple class SimpleDocument which we will be persisting to MongoDB:

package com.example.mongodb;

import org.springframework.data.document.mongodb.mapping.Document;

@Document( collection = "documents" )
public class SimpleDocument {
    private String id;
    private String name;
    private String content;

    public SimpleDocument() { 
    }

    public SimpleDocument( final String id, final String name ) {
        this.id = id;
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

Following method inserts all documents as single bulk update:

public void insert( final Collection< SimpleDocument > documents ) {  
    template.insert( documents, SimpleDocument.class );     
}

Another very cool and useful feature to explore is MongoDB’s upserts (more about this here http://www.mongodb.org/display/DOCS/Updating): if document matching specific criteria exists, it will be updated, otherwise – new document will be inserted into collection. Here is a code snipped with demonstrates it by following use case: if SimpleDocument with such name exists, it will be updated, otherwise new document will be added to collection:

@Autowired private MongoConverter converter;

public void insertOrUpdate( final SimpleDocument document ) {
    final BasicDBObject dbDoc = new BasicDBObject();
    converter.write( document, dbDoc );

    template.execute( SimpleDocument.class, 
        new CollectionCallback< Object >() {
            public Object doInCollection( DBCollection collection ) 
                    throws MongoException, DataAccessException {
                collection.update( 
                    new Query()
                        .addCriteria( new Criteria( "name" ).is( document.getName() ) )
                        .getQueryObject(), 
                    dbDoc,  
                    true, 
                    false 
                );

                return null;
            }
        }
    );
}

Please notice usage of converter bean which helps to convert Java class to MongoDB’s DBObject.

The last one I would like to show is findAndModify operation which does several things as one atomic sequence:

– find document matching criteria
– perform update
– return updated document (or old one, depending on what are your needs)

public void findAndModify( final Query query, final Update update ) {
    return template.execute( SimpleDocument.class,
        new CollectionCallback< SimpleDocument >() {
            @Override
            public SimpleDocument doInCollection( DBCollection collection ) 
                    throws MongoException, DataAccessException {
                return converter.read( SimpleDocument.class,       
                    collection.findAndModify( 
                        query.getQueryObject(), 
                        null,
                        null,
                        false,
                        update.getUpdateObject(),
                        true,
                        false
                    ) 
                );
            }
        }   
    );
}

For now, those are all interesting use cases I encountered. Honestly, I am very excited about MongoDB and strongly recommend it if it fits your application.

Reference: Exploiting MongoDB together with Spring Data project: basic concepts &  Exploiting MongoDB together with Spring Data project: advanced concepts from our JCG partner Andrey Redko at the Andriy Redko {devmind} blog.

Andrey Redko

Andriy is a well-grounded software developer with more then 12 years of practical experience using Java/EE, C#/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Oliver Gierke
Oliver Gierke
12 years ago

Hi, thanks for the introductory blog post. I have a few comments/updates regarding your code samples: 1. Spring Data MongoDB is in version 1.0 GA since end of december so you should find this version inside Maven central 2. The XML configuration you showed is unnecessarily verbose as you could have used more of the mongo namespace. The SimpleMongoDbFactory can be replaced by a namespace element, the declaration of a Mongo instance is not necessary at all in your scenario. Both MongoMappingContext and MappingMongoConverter can be replaced by a single element. So the only bean required to be configured manually… Read more »

Harshavardhan Musanalli
Reply to  Oliver Gierke

Hi Oliver,

I saw you on few github springdata projects. If you have some use cases of spring data with mongodb, please let me know.
Use case in the sense, some simple practical use cases and projects, so that I don’t need to start it from the scratch.

Peace

Alonso Isidoro Roman
11 years ago

Hi, its very interesting this technology and i d like to thank you for sharing this thoughts, but, could you share the source code?

cheers

Gaurav Ashara
Gaurav Ashara
10 years ago

Hello very nice explanation. I have some special requirement, We are using spring mongodb and for that we want to set “_id” field of Gridfs(fs.files collections)

GridFSFile file=gridOperation.store(new FileInputStream(data.getMyDoc()),data.getMyDocFileName(),data.getMyDocFileName(),metaData);

As above code line works to insert data in gridfs collections but its doesn’t provide us to programmatically generated ids . So where can i specify id for my new document.

Andriy Redko
10 years ago
Reply to  Gaurav Ashara

Hi Gaurav,

Not sure I know how to do that. You probably may specify the _id property in metaData DBObject.
I don’t see it clearly documented in Spring Data MongoDB GridFsOperations (http://docs.spring.io/spring-data/mongodb/docs/1.5.0.RELEASE/api/org/springframework/data/mongodb/gridfs/GridFsOperations.html).

Thank you.

Krunal Patel
Krunal Patel
8 years ago

Hi Andriy,

I want to add below like json string using spring mongotemplate.executeCommand() method.
“dynamic-element” : [{
“name” : ” FirstName”,
“index” : ” 0″,
“type” : ” text”,
“index-type” : ” keyword”,
“dynamic-content” : {
“language-id” : ” en_US”,
“text” : “John”
}
}, {
“name” : ” MiddleName”,
“index” : ” 0″,
“type” : ” text”,
“index-type” : ” keyword”,
“dynamic-content” : {
“language-id” : ” en_US”,
“text” : “Steve”
}
}
]

Please guide me, what will be best approach to insert above like json in mongodb?

Thanks you.

Andriy Redko
8 years ago
Reply to  Krunal Patel

Hi Krunal,

I am a bit unsure what you would like to accomplish. executeCommand is used to execute MongoDB commands, but not an arbitrary JSON. Do you want to store such documents in the collection? Would be nice to understand your intentions first. Thanks for the comment!

Best Regards,
Andriy Redko

Back to top button