Monday, July 1, 2013

Spring & MongoDB Repository

Different ways of using spring-data mongodb repository queries

Basic autogenerated queries

With spring-data you can use basic queries such as displayed below:

@Repository
public interface UserRepositoryIf extends MongoRepository {
  User findByEmail(String email);
  List<User> findByEmailLike(String email);
  List<User> findByEmailOrLastName(String email, String lastName);
}
Spring will automatically generate a JSON query based on the Syntax use use. More information about basic queries can be found in the Reference Manual. These queries are ok for basic queries and CRUD functionality. But when it comes to advanced queries then you'll see that you'll end up in a dead end very quickly.

Using queries with @Query annotation

One possibility to do advanced queries is to add the @Query annotation and work with JSON based queries. This enables you to use the full power of the MongoDB Json query language. You can parse parameters as ?0 (for first parameter), ?1 (for second parameter). In this example ?0 will contain the contents of the variable "searchText".

@Repository
public interface UserRepositoryIf 
      extends MongoRepository<User, String> {

  @Query("{\"$or\" : [" + 
    "{ \"email\" : " + 
      "{ \"$regex\" : ?0, \"$options\" : \"i\"}} , " +
    "{ \"firstName\" : " +
      "{ \"$regex\" : ?0, \"$options\" : \"i\"}}, " +
    "{ \"lastName\" : " +
      "{ \"$regex\" : ?0, \"$options\" : \"i\"}}]}")
  List findByEmailOrFirstNameOrLastNameLike(String searchText);
}
Using this we can create complex queries and have the full power of the MongoDB JSON language. However it is not type safe (mistakes in the syntax can happen very easy) and it is bound to the JSON query language of MongoDB. If you want to use another database you'll have to adjust quite a lot of code.

Using MongoTemplate

A question on Stackoverflow when to use mongoTemplate or Repositories actually resulted in a reply I liked. One of the developers of Spring pointed out that it is possible to mix queries based on the requirements by adding custom interfaces and implementations. He suggested that one should start with basic queries and as soon the queries get more complex add a custom interface and implementation to enhance the functionality with MongoTemplate. Here is the Stackoverflow question.
So if you for example have a repository called UserRepositoryIf you would add an interface called UserRepositoryIfCustom and an UserRepositoryIfImpl class. Note that the name of the interface has to be "Custom" at the end of the custom interface and for the implementation class it is expected that it is named "Impl" at the end of the class. If you use other endings it might fail.

@Repository
public interface UserRepositoryIf 
      extends MongoRepository<User, String>, 
          UserRepositoryIfCustom {
  // Basic queries
  User findByEmail(String email);
  List findByEmailLike(String email);
  List findByEmailOrLastName(String email, String lastName);
  List findByEmailOrFirstNameLike(String email, String firstName);
}
public interface UserRepositoryIfCustom {
  // Advanced query
  List<User> 
    searchByEmailOrFirstNameOrLastName(String searchPattern);
}
public class UserRepositoryIfImpl 
      implements UserRepositoryIfCustom {

  @Autowired
  private MongoTemplate mongoTemplate;
  private Class<User> clazz = User.class;

  @Override
  // Advanced query implementation
  public List<User> 
      searchByEmailOrFirstNameOrLastName(String searchPattern) {
    Query query = new Query();
    query.addCriteria(new Criteria().orOperator(
      Criteria.where("email").regex(searchPattern,"i"),
      Criteria.where("firstName").regex(searchPattern,"i"),
      Criteria.where("lastName").regex(searchPattern,"i")
    ));
    return mongoTemplate.find(query, clazz);
  }
}
As you can see like this you have a clear separation between the basic queries and the more advanced MongoTemplate queries. This is what I suggest to use and is also suggested by Spring. More information regarding MongoTemplate can be found in the Reference Manual.

QueryDSL

QueryDSL is quite advanced and totally type safe. It is another possibility how you can query MongoDB. And as you might already guess you can also extend the UserRepository interface. Even if this is quite advanced and supports different databases as backends I do not use it mainly because additional dependencies are necessary and it makes use of APT. If you want to use it please refer to the Reference Manual.

No comments:

Post a Comment