Spring Data provides a smooth, easy-to-navigate way to interact with MongoDB through its repository abstraction. This abstraction makes working with MongoDB substantially simpler by offering an extensive, higher-level interface for database operations. So, in this topic, let's examine the MongoRepository interface and look at its features.
MongoRepository interface
MongoRepository is a unique extension of the repository pattern that Spring Data provides for MongoDB. Its extension comprises CrudRepository, PagingAndSortingRepository, and QueryByExampleExecutor.
The CrudRepository provides methods for CRUD operations, which include recovering, saving, updating, and erasing single or multiple entities from the database. The PagingAndSortingRepository has methods that can find entities using pagination and sorting, and the QueryByExampleExecutor supports executing queries by creating domain object instances.
All these interfaces are data store-independent, and MongoRepository inherits all their methods. Besides, MongoRepository includes several MongoDB-specific methods for optimized insertion of new documents. You can learn about these in the following sections.
Interface declaration
To include MongoRepository in your project, you should add the spring-boot-starter-data-mongodb starter to the build file.
Similar to other Spring Data repositories, you need a domain entity and an interface that extends MongoRepository to work with the entities. In this topic, we'll use the following entity:
@Document(collection = "fleet")
public record Starship(
@Id
String id,
String name,
Integer crew
) { }Then you can declare an interface that extends the MongoRepository<T, ID> with the parameters Starship as the entity class (T) and String as the entity ID class (ID):
public interface StarshipRepository extends MongoRepository<Starship, String> { }The interface offers many vital methods right away, and you can also introduce new methods to it without implementing them yourself. Examples of this will be provided in the next sections.
CRUD operations
MongoRepository includes methods for all standard create, read, update, and delete (CRUD) operations without needing to write any implementation code. As a child of CrudRepository, it inherits all its methods and adds two more. Here are a few key methods (but not all of them):
Create/Update Operations
S save(S entity): This saves a given entity. If the entity already exists, it will update the existing document and return the saved entity.List<S> saveAll(Iterable<S> entities): This saves all given entities. It will update existing documents if they already exist, and it returns the saved entities.S insert(S entity): This inserts the given entity. It assumes that the entity is new and applies insertion optimizations.List<S> insert(Iterable<S> entities): This inserts the given entities. It assumes the entities to be new and applies insertion optimisations.
Read Operations
Optional<T> findById(ID id): This retrieves an entity by its ID. It returns anOptionalwhich can benullif no entity with the given id exists.List<T> findAll(): This method returns all instances of the type.long count(): This method returns the number of available entities.
Delete Operations
void deleteById(ID id): This deletes the entity with the given id.void delete(T entity): This deletes a given entity.void deleteAll(): This deletes all the entities managed by the repository.
Keep in mind that T is the type the repository manages, and ID is the id type of the managed entity. These methods are directly available for use, no need for you to implement them, thanks to Spring Data's repository structure.
Let's apply our newfound knowledge and set up a demo project. We'll create a command-line runner component that will execute when the application starts. It will clear the collection and then insert some entities into it:
@Component
class Runner implements CommandLineRunner {
private final StarshipRepository repository;
public Runner(StarshipRepository repository) {
this.repository = repository;
}
@PostConstruct
public void init() {
repository.deleteAll();
var starships = List.of(
new Starship(null, "Voyager Nebula", 30),
new Starship(null, "Quantum Phoenix", 256),
new Starship(null, "Stellar Serenity", 2),
new Starship(null, "Galactic Pegasus", 1500),
new Starship(null, "Andromeda Odyssey", 3000),
new Starship(null, "Nebula Nomad", 40),
new Starship(null, "Celestial Falcon", 30),
new Starship(null, "Galactic Galleon", 1500)
);
repository.saveAll(starships);
}
@Override
public void run(String... args) {
// other operations go here...
}
}Property expressions
MongoRepository allows you to declare methods in the repository using what's called property expressions. Spring Data will then execute the corresponding queries. For instance, we can announce a method in our StarshipRepository interface that finds all Spaceship entities whose name begins with a certain substring:
public interface StarshipRepository extends MongoRepository<Starship, String> {
List<Starship> findAllByNameStartingWith(String prefix);
}If we call this method in our demo application:
@Override
public void run(String... args) {
List<Starship> galacticShips = repository.findAllByNameStartingWith("Galactic");
System.out.println("=> Ships whose names start with 'Galactic':");
galacticShips.forEach(System.out::println);
// other code goes here...
}It will provide the following output:
=> Ships whose names start with 'Galactic':
Starship[id=6568555f1df0f61100c7fe54, name=Galactic Pegasus, crew=1500]
Starship[id=6568555f1df0f61100c7fe58, name=Galactic Galleon, crew=1500]You can find a list of all supported keywords for generating property expressions in the official guide.
Custom queries
The MongoRepository supports custom queries via the @Query annotation. In our next example, we will announce a new method in the StarshipRepository that fetches all entities with a crew equal to or greater than a certain value:
public interface StarshipRepository extends MongoRepository<Starship, String> {
List<Starship> findAllByNameStartingWith(String name);
@Query("{'crew' : {$gte: ?0}}")
List<Starship> findBigShips(int minimumCrew);
}The @Query annotation accepts a string value with a MongoDB query. In our case, it filters documents based on the crew field to find all documents with a crew equal to or greater than the method argument.
If we call this method in our demo application:
@Override
public void run(String... args) {
List<Starship> bigShips = repository.findBigShips(100);
System.out.println("=> Ships with a crew of 100 or more:");
bigShips.forEach(System.out::println);
}It will print the following output:
=> Ships with a crew of 100 or more:
Starship[id=6568555f1df0f61100c7fe52, name=Quantum Phoenix, crew=256]
Starship[id=6568555f1df0f61100c7fe54, name=Galactic Pegasus, crew=1500]
Starship[id=6568555f1df0f61100c7fe55, name=Andromeda Odyssey, crew=3000]
Starship[id=6568555f1df0f61100c7fe58, name=Galactic Galleon, crew=1500]Paging and sorting
The MongoRepository supports paging and sorting right out of the box. In our next example, we will fetch two pages from the collection, sorted by the crew field in descending order with a page size of 3:
@Override
public void run(String... args) {
Sort sort = Sort.by(Sort.Order.desc("crew"));
Pageable pageable = PageRequest.of(0, 3, sort);
Page<Starship> page = repository.findAll(pageable);
System.out.println("=> First page:");
page.getContent().forEach(System.out::println);
pageable = page.nextPageable();
page = repository.findAll(pageable);
System.out.println("=> Second page:");
page.getContent().forEach(System.out::println);
}The above code will print the following:
=> First page:
Starship[id=6568555f1df0f61100c7fe55, name=Andromeda Odyssey, crew=3000]
Starship[id=6568555f1df0f61100c7fe58, name=Galactic Galleon, crew=1500]
Starship[id=6568555f1df0f61100c7fe54, name=Galactic Pegasus, crew=1500]
=> Second page:
Starship[id=6568555f1df0f61100c7fe52, name=Quantum Phoenix, crew=256]
Starship[id=6568555f1df0f61100c7fe56, name=Nebula Nomad, crew=40]
Starship[id=6568555f1df0f61100c7fe57, name=Celestial Falcon, crew=30]Query by example
Lastly, MongoRepository supports the Query by Example technique. This technique uses a simple interface that supports dynamic query creation without any need to write queries containing field names. Instead, you create a prototype instance of the object and execute the query based on the fields of the object:
@Override
public void run(String... args) {
Starship probe = new Starship(null, null, 30);
Example<Starship> example = Example.of(probe);
List<Starship> found = repository.findAll(example);
System.out.println("=> Query by Example:");
found.forEach(System.out::println);
}This code creates a Starship class instance with certain field values and then builds an Example<Starship> object based on that instance. After that, it passes the example to the findAll parameter method, and the repository returns all entities matching the example. Running this code will print the following:
=> Query by example:
Starship[id=6568555f1df0f61100c7fe51, name=Voyager Nebula, crew=30]
Starship[id=6568555f1df0f61100c7fe57, name=Celestial Falcon, crew=30]Conclusion
This topic focused on the MongoRepository interface and the query methods it provides. We provided examples of using built-in CRUD methods, declaring custom query methods using property expressions and the @Query annotations, implementing paging and sorting, and demonstrating how to use the Query by Example technique.