Monday, 26 September 2011

Spring + MongoDB: Type Safe Queries

In this blog post I am going to walkthrough how you can use QueryDSL to provide type-safe queries when using Spring and MongoDB.

Maven Setup
I am assuming you are starting from a blank sheet so the first thing to do is to create a new Maven project and that has the dependencies on spring-data-mongodb, querydsl-apt and querydsl-mongodb.

To pull these dependencies down you will need to include the Spring Data repository (http://maven.springframework.org/milestone) and QueryDSL repository (http://source.mysema.com/maven2/releases).

The way QueryDSL works is by generating query classes based upon your persistence domain model. QueryDSL has a simple maven plugin that scans for appropriate annotations and generates the query classes for you. You will need to add this plugin within your pom.

My entire maven pom can for found at: pom.xml

Domain Model
Let’s create a simple Product class that has a String property (sku):

@QueryEntity
public class Product {                      

    private String sku;           
   
    public Product(String sku) {                  
        this.sku = sku;   
    }                 

    //… getter and setters
}

The full class can be found at: Product.java

Notice I marked the class with the @QueryEntity annotation. This annotation will be used be the QueryDSL pre-processor to identify the need to generate a Query based version of this class.

Spring Data Repository
Spring Data provides a framework that supports a number of different types of NoSQL stores as well as the your traditional relational stores.  In this example I am going to define a simple MongoDB Repository for my Product.

The first thing to do is to create an interface called “ProductRepository” that extends MongoRepository and QueryDslPredicateExecutor.

public interface ProductRepository extends MongoRepository<Product, String>, QueryDslPredicateExecutor<Product> {

}

A full copy of my ProductRepository can be found at: ProductRepository.java

You don’t need to worry about the implementation of this interface as Spring Data will provide this for you. To make this example simple I am going to just use the methods provided by the inherited interfaces however if would like to add you own methods see http://static.springsource.org/spring-data/data-document/docs/current/reference/html/#mongodb.repositories.queries

Spring Configuration
Lets wire up Spring so that the ProductRepository points to your MongoDB.

Creating a new client instance for mongodb is straightforward as a convince namespace is provided by Spring Data.

  <mongo:mongo id="mongo" />

I am happy to use the default mongodb driver settings however you can add configuration attributes if you require.

The next thing to do is to create a MongoTemplate bean that contains your instance of mongo and the name of your mongodb database:

<bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">        
     <constructor-arg ref="mongo" />   
     <constructor-arg value="products" />  
</bean>

The final thing to configure is a reference to the location of our Spring Data Repositories. To make configuration simple, Spring Data will scan a given package:

<mongo:repositories base-package="charris.example.repositories" />

My full Spring configuration can be found at springdata-context.xml

Main App
I am going to create a basic application that creates a Product with a known sku and then inserts it into MongoDB. I will then execute a type-safe query to get the product back out and for completeness will then clear the MongoDB collection. Lets break this down into simple steps:

1)   Getting the hold of the Repository

There is nothing unexpected here; basically we get hold of the Spring Application Context and lookup the ProductRepository

AbstractApplicationContext context = new ClassPathXmlApplicationContext("springdata-context.xml");

ProductRepository repo = context.getBean(ProductRepository.class);

2)   Saving a Product

To save a Product into the Repository, simply create an instance of Product and pass the instance to the repository’s save method

repo.save(new Product("SKU-12"));

3)   Query the Repo

The power of QueryDSL kicks in when we look to query MongoDB. In order to retrieve the Product, I simply use the QProduct generated by QueryDSL to navigate my Product and then execute “eq” on “sku” property.

Product p = repo.findOne(QProduct.product.sku.eq("SKU-12"));                   

The QProduct gives me the type-safe query I was looking for and the maven plugin will keep it in sync with my Product class.

4)   Clean Down

As this is a simple test application, I would also like to remove the Product I inserted:


repo.deleteAll();

The full source of this example can be found at:


Enjoy J

6 comments:

  1. Hi Chris,

    very cool intro from top bottom. Just one minor thing to add: instead of the Querydsl specific @QueryEntity you could also simply use @Document. This would reuire the usage of our custom annotation processor class in pom.xml of course.

    Beyond that for your use case it's not really required to extend MongoRepository with your repository interface. CrudRepository would have been sufficient here. I know it's not a big deal but some people might want to avoid exposing the store they save stuff to from their repository interfaces.

    Cheers,
    Ollie

    ReplyDelete
    Replies
    1. Hi Ollie,

      you are talking about an annotation processor that should be in spring-data-mongodb, but whats its name? The reference documentation is not that helpful.

      Cheery,

      Phillip

      Delete
    2. org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor

      Delete
  2. how create app mongodb one to many relationship with spring

    ReplyDelete
  3. Do you know how you would create a Query object with this setup. I want to be able to do the following.

    if(someName != null){
    query.where(QMyClass.name.eq(someName));
    }
    if(someTime != null){
    query.where(QMyClass.time.eq(someTime));
    }

    List result = query.list();

    I've tried looking at the MongodbQuery but I couldn't get it to work. Any ideas?

    ReplyDelete
    Replies
    1. I think that it could solve your Question

      http://stackoverflow.com/questions/10802936/creating-a-complex-query-using-querydsl-and-mongodb-in-java

      Delete