MongoRepository CRUD
MongoRepository<T, ID> is the declarative way to persist documents. You extend the interface, Spring generates the implementation at runtime, and you immediately get CRUD plus pagination, sorting, and derived query methods — the same model as JpaRepository.
The repository interface
Extend MongoRepository<T, ID> where T is your @Document type and ID is the @Id type (usually String).
public interface ProductRepository extends MongoRepository<Product, String> {
}
That alone gives you the inherited CRUD surface:
| Method | Purpose |
|---|---|
save(T) / saveAll(...) | Insert or replace a document |
findById(ID) | Optional<T> lookup by _id |
findAll() / findAll(Sort) | Read all, optionally sorted |
findAll(Pageable) | Page through results |
existsById(ID) | Cheap existence check |
count() | Document count |
deleteById(ID) / delete(T) | Remove a document |
The document
@Document("products")
public record Product(@Id String id, String name, String category, BigDecimal price) { }
Derived query methods
Spring parses method names and builds the MongoDB query for you — findBy + property + optional keyword.
public interface ProductRepository extends MongoRepository<Product, String> {
List<Product> findByCategory(String category);
List<Product> findByPriceLessThan(BigDecimal max);
List<Product> findByNameContainingIgnoreCase(String fragment);
Optional<Product> findFirstByCategoryOrderByPriceAsc(String category);
long countByCategory(String category);
void deleteByCategory(String category);
}
| Method | MongoDB filter |
|---|---|
findByCategory | { "category": ?0 } |
findByPriceLessThan | { "price": { "$lt": ?0 } } |
findByPriceBetween | { "price": { "$gte": ?0, "$lte": ?1 } } |
findByCategoryAndPrice | { "category": ?0, "price": ?1 } |
For anything more complex, write a JSON @Query or use MongoTemplate. See Queries & @Query.
A service layer
Keep business logic in a @Service and inject the repository via the constructor.
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository repository;
public Product create(Product product) {
return repository.save(product);
}
public Product getById(String id) {
return repository.findById(id)
.orElseThrow(() -> new ProductNotFoundException(id));
}
public List<Product> byCategory(String category) {
return repository.findByCategory(category);
}
public Product update(String id, Product changes) {
Product existing = getById(id);
Product merged = new Product(existing.id(), changes.name(),
changes.category(), changes.price());
return repository.save(merged);
}
public void delete(String id) {
repository.deleteById(id);
}
}
Note:
saveperforms an upsert by_id: if the document’s id is null it inserts and returns the generated id; if the id exists it replaces the whole document. To update only specific fields, useMongoTemplate’s partial updates — see MongoTemplate.
A REST controller
Expose the service over HTTP with a @RestController.
@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService service;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product create(@RequestBody Product product) {
return service.create(product);
}
@GetMapping("/{id}")
public Product getOne(@PathVariable String id) {
return service.getById(id);
}
@GetMapping
public List<Product> byCategory(@RequestParam String category) {
return service.byCategory(category);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable String id) {
service.delete(id);
}
}
The exchange
Creating a product:
POST /api/products
Content-Type: application/json
{ "name": "Keyboard", "category": "peripherals", "price": 79.99 }
Output:
{
"id": "65f1c2a8e4b0a1d2c3e4f567",
"name": "Keyboard",
"category": "peripherals",
"price": 79.99
}
The generated id is MongoDB’s _id (an ObjectId serialized as a string). Querying by category returns an array:
GET /api/products?category=peripherals
[
{ "id": "65f1c2a8e4b0a1d2c3e4f567", "name": "Keyboard", "category": "peripherals", "price": 79.99 },
{ "id": "65f1c2a8e4b0a1d2c3e4f568", "name": "Mouse", "category": "peripherals", "price": 39.50 }
]
Tip: Map documents to DTOs before returning them when the document holds fields you do not want to expose. See Entity vs DTO.