Spring Data Repository for MongoDB

First, I have to mention that I am highly impressed with almost everything I find in Spring. So I should not have been so surprised about how amazing Spring Data is and it’s integration with Mongo. Now to the interesting stuff.

I have multiple databases with multiple collections all on the same mongo server. I could not find an example anywhere that accomplished this. Here is how I did it:

To begin, you have to add one dependency. I use maven, so here is the maven dependency:

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

Now, you need to configure your mongo connection like usual. I like to just put it in the application-context.xml:

<mongo:mongo id="mongoConnection" host="my.mongo.server" port="12345" write-concern="1"/>

One thing to mention here is write-concern. It defaults to 0 which is basically fire and forget, and it will not return an error if one is thrown. Here is the API page with the available values:
http://api.mongodb.org/java/2.5/com/mongodb/WriteConcern.html

Next, configure a template for each database.

    <bean id="imageTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoConnection"/>
        <constructor-arg name="databaseName" value="imagedatabase"/>
    </bean>

    <bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoConnection"/>
        <constructor-arg name="databaseName" value="vehicledatabase"/>
    </bean>

Now, you need to tell Spring where your repositories are so it can inject them. They must all be in the same directory. I tried to have them in different sub-directories, and it did not work correctly. So they are all in the repository directory.

    <mongo:repositories base-package="my.package.repository">
        <mongo:repository id="imageRepository" mongo-template-ref="imageTemplate"/>
        <mongo:repository id="carRepository" mongo-template-ref="vehicleTemplate"/>
        <mongo:repository id="truckRepository" mongo-template-ref="vehicleTemplate"/>
    </mongo:repositories>

Each repository is an Interface and is written as follows (yes, you can leave them blank):

@Repository
public interface ImageRepository extends MongoRepository<Image, String> {
}
@Repository
public interface TruckRepository extends MongoRepository<Truck, String> {
}

NOTE: If you have a custom query, here is some crazy magic for you. Just add a method to the interface, spring knows how to interpret it! Here is an example:

@Repository
public interface CarRepository extends MongoRepository<Car, String> {
    Car findByModelAndYearAndLicensePlate(String model, String year, String licensePlate);
}

Here is a table with the expected keywords from the Spring documentation:

Keyword Sample Logical Result
GreaterThan findByAgeGreaterThan(int age) {“age” : {“$gt” : age}}
LessThan findByAgeLessThan(int age) {“age” : {“$lt” : age}}
Between findByAgeBetween(int from, int to) {“age” : {“$gt” : from, “$lt” : to}}
IsNotNull, NotNull findByFirstnameNotNull() {“age” : {“$ne” : null}}
IsNull, Null findByFirstnameNull() {“age” : null}
Like findByFirstnameLike(String name) {“age” : age} ( age as regex)
(No keyword) findByFirstname(String name) {“age” : name}
Not findByFirstnameNot(String name) {“age” : {“$ne” : name}}

Here is one of the java objects:

@Document
public class Image {

    @Id
    private String id;

    private Date createTime;
    private Date updateTime;

    @Field(value = "fName")
    private String fileName;
    private byte[] imageData;

    //Getters and setters

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
}

You do not have to annotate the POJOs with @Document, but it helps with efficiency because it allows the classpath scanner to find and pre-process your domain objects to extract the necessary metadata. Visit this blog for more details: http://krams915.blogspot.com/2011/04/spring-data-mongodb-revision-for-100m2.html

The @Id annotation will tell spring to make that property an ObjectId and give it the _id name which is a Mongo standard. So you can make it whatever type and name you want in java, but it will convert it back and forth between java and mongo.

The @Field annotation is used if you want to save the name differently in mongo

Now, here is one of the really cool things about Spring Data Mongo. The name of the POJO is the collection! Image.java will be saved to the image collection within the imagedb database.

Here is how you can find, insert, and delete records:

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;

    public Image findById(String id) {
        return imageRepository.findOne(id);
    }

    public List getList() {
        return imageRepository.findAll();
    }

    public Image insert(Image image) {
        //Make sure id is null, so mongo will generate an id, if it is not null, then mongo will use whatever you set it to, even an empty string
        image.setId(null);
        
        return imageRepository.save(image);
    }

    public Image update(Image image) {
        image.setUpdateTime(new Date());
        return imageRepository.save(image);
    }

    public void delete(String id) {
        imageRepository.delete(id);
    }
}

That is all you need. Finally, we do not have to write basic data access code. I love it when code is smart!

About these ads

6 responses to “Spring Data Repository for MongoDB

  1. Can you show how to use the ImageService from a driver class? I can make it work from a test class by having my test class extend AbstractJunit4SpringContextTests, but not from a main(). Something like:

    public Class AppDriver {
    public static void main(String[] args) {
    ImageService service = new ImageService();
    }
    }

  2. In order to use a Spring bean, it has to be instantiated from a Spring context. Using Spring MVC, you don’t normally have to do anything special once it’s setup. But, in some cases where it might not be a full Spring application all the way through, you can use something like this:

    ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext-daemon.xml”);
    ImageService imgService = context.getBean(“imageService”, ImageService.class);

    Hope this helps.

  3. Hi, the is giving the following error:
    content was found starting with element ‘mongo:repository’. One of ‘{“http://www.springframework.org/schema/data/reposiory”:include-filter, “http://www.springframework.org/schema/data/repository”:exclude-filter}’ is expected.

    here is how I have declared the repositories:

    Please correct me where I am doing wrong.

  4. Hi not sure why the code snippet is not shown in my above post. Can you please share the full data-source.xml for the example you have given. What all namespace I need to import.

  5. Like this, however if you are using 1.1.0.RC1, this will not work. You will need to define each repository separately. To avoid duplicate collection creation in the databases, simply exclude from the repository you do not want the collection to be included in (use repository:exclude-filter).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s