How to Create a Custom MongoDB Spring Data Repository

Spring Data Mongo repositories are great. They allow developers to build 90% of their features using the power of query methods. But sometimes, you won’t be able to rely on the generated query methods and will need to develop a custom implementation. In this article, you will discover how to create custom Spring Data Mongo repositories where you can implement the data access methods yourself.

Photo by Kevin Ku: https://www.pexels.com/photo/data-codes-through-eyeglasses-577585/

⚠️ Before reading this article, I want to set some expectations straight. I assume you have some experience with Spring Data repositories, as this post will focus on adding methods with a custom implementation.

The Airport Entity

Let’s assume we need to store airports in a Mongo database. We can model our airport like you see in the snippet below.

@Document
public record Airport(String id,
                      String icaoCode,
                      @TextIndexed String name,
                      int nbRunways) {
}

A Standard Spring Mongo Repository

Defining a Mongo Repository is very easy. You can create a new interface and extend Repository<T, ID> or an interface that extends it, such as CrudRepository<T, ID>. To access Mongo-specific features, you can extend MongoRepository<T, ID>. After that, you can add query method signatures to the interface, and Spring will take care of the rest.

public interface AirportRepository extends MongoRepository<Airport, String> {
    
   // Add query methods...
   List<Airport> findByName(String name);
}

Adding Methods With Custom Implementations

To create a custom Spring Mongo Repository, you can follow the following process:

  • Define a custom repository interface containing the methods that you wish to implement yourself

  • Implement the custom repository interface

  • Make your standard entity repository extend the custom repository interface

Let’s see this process in action.

Defining a custom repository interface

public interface CustomAirportRepository {
    // This is not a query method, we need to implement it
    List<Airport> findByFullTextSearch(String text);
}

The method defined in this interface is called findByFullTextSearch, and it is not a query method. Spring will not know how to generate a proxy implementation for it, so we must provide one ourselves.

Implementing a custom repository interface

class CustomAirportRepositoryImpl implements CustomAirportRepository {
    private final MongoTemplate mongoTemplate;

    public CustomAirportRepositoryImpl(MongoTemplate mongoTemplate) {
        this.mongoTemplate= mongoTemplate;
    }

    @Override
    public List<Airport> findByTextSearch(String text) {
        var criteria = TextCriteria
                .forDefaultLanguage()
                .matchingPhrase(text);

        var query = TextQuery.queryText(criteria).sortByScore();

        return this.mongoTemplate.find(query, Airport.class);
    }
}

I created a new class called CustomAirportRepositoryImpl, which implements our custom interface. This class uses a MongoTemplate instance to perform a full-text search based on the provided text.

Entity repository extends custom repository interface

There is one last thing we need to do to put all these puzzle pieces together. We also need to make the standard entity interface (AirportRepository) extend our custom interface.

// Standard interface also extends CustomAirportRepository
public interface AirportRepository extends 
       MongoRepository<Airport, String>,
       CustomAirportRepository {
    
   List<Airport> findByName(String name);
}

And what happens next is magical. Spring will use your custom implementation for the methods declared in the custom interfaces when generating the proxy methods.

// We still use the standard repository interface
private final AirportRepository repository;

// When we call findByTextSearch, the custom implementation is used
var result = repository.findByTextSearch("Heathrow");
Previous
Previous

Integrating Bounded Contexts for DDD Beginners

Next
Next

How to Perform Multiple HTTP Requests in Angular