EzyMongo Introduction

1. Introduct to EzyMongo

EzyMongo (Easy going to Mongo Interaction) is a framework support to interact to MongoDB, It supports:
  1. Transparently map your Java entities to MongoDB documents and back
  2. Query, update, delete and aggregation by query string
  3. Annotation driven

2. Structure of EzyMongo

  1. Database Context: wraps MongoClient, DataBinding. Manages repositiors, QueryManager and DataSerializers. Provides function to get repostiories, queries and serializers
  2. Repostiories: Manages all repostiores by name and type
  3. QueryManager: Manages all queries, collects queries from annotations and deverloper set
  4. DataSerializers: Manages all data serializers and deserializers, provide a way to allow deverloper custom and set their serializers and deverlopers
  5. Repostiory: converts queries, data and calls to MongoDB

3. Install EzyMongo

1. To create EzyMongo we need add dependency
<dependency>
    <groupId>com.tvd12</groupId>
    <artifactId>ezydata-mongodb</artifactId>
    <version>1.2.0</version>
</dependency>
The latest version can be found in the Maven Central repository.
2. To use EzyMongo you need config to create repositories
You can use ezyfox-boot-autoconfigure by add to your pom.xml:
<dependency>
    <groupId>com.tvd12</groupId>
    <artifactId>ezyfox-boot-autoconfigure</artifactId>
    <version>1.0.1</version>
</dependency>
Or because with some reason, you can not use ezyfox-boot-autoconfigure you can config like this:
import com.mongodb.MongoClient;
import com.tvd12.ezydata.database.EzyDatabaseContext;
import com.tvd12.ezydata.mongodb.EzyMongoDatabaseContextBuilder;
import com.tvd12.ezydata.mongodb.loader.EzySimpleMongoClientLoader;
import com.tvd12.ezyfox.annotation.EzyProperty;
import com.tvd12.ezyfox.bean.EzyBeanConfig;
import com.tvd12.ezyfox.bean.EzyPackagesToScanAware;
import com.tvd12.ezyfox.bean.EzySingletonFactory;
import com.tvd12.ezyfox.bean.EzySingletonFactoryAware;
import com.tvd12.ezyfox.bean.annotation.EzyConfigurationBefore;
import com.tvd12.ezyfox.util.EzyPropertiesAware;
import lombok.Setter;

import java.util.Map;
import java.util.Properties;

@Setter
@EzyConfigurationBefore
public class EzyMongoConfiguration implements
        EzyBeanConfig,
        EzyPropertiesAware,
        EzySingletonFactoryAware {
    
    @EzyProperty("database.mongo.database")
    private String databaseName;
    
    private Properties properties;
    
    private EzySingletonFactory singletonFactory;
    
    @Override
    public void config() {
        EzyDatabaseContext databaseContext = newMongodbDatabaseContext();
        Map<String, Object> repos = databaseContext.getRepositoriesByName();
        for (String repoName : repos.keySet()) {
            singletonFactory.addSingleton(repoName, repos.get(repoName));
        }
    }
    
    private EzyDatabaseContext newMongodbDatabaseContext() {
        EzyMongoDatabaseContextBuilder builder = new EzyMongoDatabaseContextBuilder()
                .properties(properties)
                .scan("your_package_to_scan")
                .mongoClient(newMongoClient())
                .databaseName(databaseName);
        return builder.build();
    }
    
    protected MongoClient newMongoClient() {
        return EzySimpleMongoClientLoader.load(properties);
    }
}

4. Example

Let’s say we need create an http server application to manage a book store (Category, Author and Book), we need 6 apis
  • api/v1/author/add: Add an author
  • api/v1/category/add: Add a category
  • api/v1/book/add: Add a book
  • api/v1/books/{bookId}: Get a book by id
  • api/v1/books: Get a list of books
  • apiv1/books/expected-revenue:
4.1 Create entities class
To store Category, Author and Book to MongoDB, we need create 3 entities
import com.tvd12.ezydata.database.annotation.EzyCollection;
import com.tvd12.ezyfox.annotation.EzyId;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@EzyCollection
@AllArgsConstructor
@NoArgsConstructor
public class Category {
    @EzyId
    private long id;
    private String name;
}

@Getter
@Setter
@EzyCollection
@AllArgsConstructor
@NoArgsConstructor
public class Author {
    @EzyId
    private long id;
    private String name;
}

@Getter
@Setter
@EzyCollection
@AllArgsConstructor
@NoArgsConstructor
public class Book {
    @EzyId
    private Long id;
    private Long categoryId;
    private Long authorId;
    private String name;
    private BigDecimal price;
    private LocalDate releaseDate;
    private LocalDateTime releaseTime;
}
4.2 Create repositories
We need create 3 repositories for 3 entities
import com.tvd12.ezydata.example.mongo.entity.Category;
import com.tvd12.ezydata.mongodb.EzyMongoRepository;
import com.tvd12.ezyfox.database.annotation.EzyRepository;

@EzyRepository
public interface CategoryRepository extends EzyMongoRepository<Long, Category> {
    Category findByName(String name);
}
import com.tvd12.ezydata.example.mongo.entity.Author;
import com.tvd12.ezydata.mongodb.EzyMongoRepository;
import com.tvd12.ezyfox.database.annotation.EzyRepository;

@EzyRepository
public interface AuthorRepository extends EzyMongoRepository<Long, Author> {
}
import java.util.List;

import com.tvd12.ezydata.database.annotation.EzyQuery;
import com.tvd12.ezydata.example.mongo.entity.Book;
import com.tvd12.ezydata.example.mongo.result.SumBookPriceResult;
import com.tvd12.ezydata.mongodb.EzyMongoRepository;
import com.tvd12.ezyfox.database.annotation.EzyRepository;
import com.tvd12.ezyfox.util.Next;

@EzyRepository
public interface BookRepository extends EzyMongoRepository<Long, Book> {

    Book findByNameAndAuthorId(String name, Long authorId);

    @EzyQuery("{$orderby:{name:1}}")
    List<Book> findBooks(Next next);

    @EzyQuery("{$query:{name:{$lt:?0}}, $orderby:{name:1}}")
    List<Book> findByNameLt(String name, Next next);

    @EzyQuery("{$query:{name:{$gt:?0}}, $orderby:{name:1}}")
    List<Book> findByNameGt(String name, Next next);

    @EzyQuery("[{ $group: { _id : 'sum', sum : { $sum: {$toDecimal: '$price'}} } }]")
    SumBookPriceResult sumPrice();
}
4.3 Save entities
Save a category, see more details
Category category = new Category(
    maxIdRepository.incrementAndGet("category"),
    request.getCategoryName()
);
categoryRepository.save(category);
Save an author, see more details
Author author = new Author(
    maxIdRepository.incrementAndGet("author"),
    request.getAuthorName()
);
authorRepository.save(author);
return author;
Save a book, see more details
val bookId = maxIdRepository.incrementAndGet("book");
val book = requestToEntityConverter.toBookEntity(request, bookId);
bookRepository.save(book);
4.4 Query data
Find a category by id, see more details
Category category = categoryRepository.findById(request.getCategoryId());
Find an author by id, see more details
Author author = authorRepository.findById(request.getAuthorId());
Find a book by name and author id, see more details
Book existedBook = bookRepository.findByNameAndAuthorId(
    request.getBookName(),
    request.getAuthorId()
);
Get sum of all book prices, see BookRepository and BookController to get more details
@EzyQuery("[{ $group: { _id : 'sum', sum : { $sum: {$toDecimal: '$price'}} } }]")
SumBookPriceResult sumPrice();
bookRepository.sumPrice().getSum()
Full source code available on Github

5. Conclusion

With EzyMongo you will work with MongoDB lot easier and more native. With query string, you will don't need care about bson, criteria and you will reduce a lot of your source code