MongoTemplate
MongoTemplate is the low-level workhorse of Spring Data MongoDB. Where MongoRepository is declarative, MongoTemplate gives you imperative control: build queries with Query/Criteria, apply partial updates without replacing the whole document, and run atomic find-and-modify operations.
Injecting MongoTemplate
The starter auto-configures a MongoTemplate bean. Inject it via the constructor.
@Service
@RequiredArgsConstructor
public class ProductSearch {
private final MongoTemplate mongoTemplate;
}
The Query / Criteria API
Query holds a filter built from Criteria plus options like sort, limit, and skip. This is the programmatic equivalent of a MongoDB filter document.
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
Query q = query(where("category").is("peripherals")
.and("price").lt(new BigDecimal("50")))
.with(Sort.by(Sort.Direction.ASC, "price"))
.limit(10);
List<Product> cheap = mongoTemplate.find(q, Product.class);
That builds the filter:
{ "category": "peripherals", "price": { "$lt": 50 } }
| Criteria method | MongoDB operator |
|---|---|
.is(v) | equality |
.ne(v) | $ne |
.lt / .lte / .gt / .gte | $lt / $lte / $gt / $gte |
.in(...) | $in |
.regex("^A") | $regex |
.exists(true) | $exists |
Common read helpers: findOne, find, findById, count, and exists.
Update operations
The big advantage over repository.save() is partial updates — you change specific fields without sending the entire document. Build an Update and target it with a Query.
import org.springframework.data.mongodb.core.query.Update;
Update update = new Update()
.set("price", new BigDecimal("69.99"))
.inc("views", 1)
.currentDate("updatedAt");
Update method | Effect |
|---|---|
.set(field, value) | $set — assign a value |
.unset(field) | $unset — remove a field |
.inc(field, delta) | $inc — increment numerically |
.push(field, value) | $push — append to an array |
.pull(field, value) | $pull — remove from an array |
.currentDate(field) | $currentDate — set to server time |
updateFirst vs updateMulti
updateFirst modifies the first matching document; updateMulti modifies all matches. Both return an UpdateResult with counts.
// Mark one product on sale
mongoTemplate.updateFirst(
query(where("name").is("Keyboard")),
new Update().set("onSale", true),
Product.class);
// Apply a 10% increase to an entire category
mongoTemplate.updateMulti(
query(where("category").is("peripherals")),
new Update().mul("price", 1.10),
Product.class);
Output (UpdateResult):
matchedCount=2, modifiedCount=2
upsert
upsert updates a matching document, or inserts a new one if none matches — atomically.
mongoTemplate.upsert(
query(where("sku").is("KB-001")),
new Update().set("name", "Mechanical Keyboard").set("price", 89.99),
Product.class);
Tip: Use
upsertfor idempotent imports and counters where you do not know whether the document already exists. Combined with.inc()it gives you safe, atomic “create-or-increment” semantics.
findAndModify — atomic read-then-write
findAndModify applies an update and returns the document in a single atomic operation, avoiding the race between a separate read and write. By default it returns the pre-update state; pass FindAndModifyOptions to return the new one.
import org.springframework.data.mongodb.core.FindAndModifyOptions;
Product reserved = mongoTemplate.findAndModify(
query(where("sku").is("KB-001").and("stock").gt(0)),
new Update().inc("stock", -1),
FindAndModifyOptions.options().returnNew(true),
Product.class);
This safely decrements stock only when units remain and returns the updated document — a classic pattern for inventory reservation.
Warning: Without
returnNew(true),findAndModifyreturns the document as it looked before the update. Set it explicitly when you need the post-update values.
When to choose MongoTemplate
- You need partial updates instead of full-document replacement.
- The query is dynamic (filters assembled at runtime).
- You need atomic
findAndModify, bulk operations, or aggregation pipelines — see Aggregation Framework.
For straightforward CRUD, stick with MongoRepository; the two work side by side on the same documents.