Skip to content
Spring Boot sb mongodb 3 min read

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 methodMongoDB 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 methodEffect
.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 upsert for 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), findAndModify returns 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.

Last updated June 13, 2026
Was this helpful?