MongoDB with Spring Data project
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.
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 »
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
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
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.
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.
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.
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