Skip to content
Spring Boot sb mongodb 3 min read

@Document Mapping

Spring Data MongoDB maps your Java classes to BSON documents using mapping annotations from the org.springframework.data.mongodb.core.mapping package. The model mirrors JPA’s @Entity/@Id but the annotations and storage semantics differ.

@Document and the collection

@Document marks a class as persistable and names its collection. Without the collection attribute, Spring uses the decapitalized class name (Productproduct).

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

@Document(collection = "products")
public class Product {

    @Id
    private String id;

    @Field("product_name")
    private String name;

    private BigDecimal price;

    // constructors, getters, setters
}

Note: @Id here is org.springframework.data.annotation.Id, not jakarta.persistence.Id. Importing the JPA one is a common mistake that leaves your _id unmapped.

The @Id field

The @Id field maps to MongoDB’s _id. If you use a String and leave it null, MongoDB generates an ObjectId and Spring stores its hex string back into your field on save.

Java typeStored asNotes
StringObjectId (or string)Most common; auto-generated when null
ObjectIdObjectIdUse the driver’s org.bson.types.ObjectId directly
Long / Integernumeric _idYou must assign it yourself
Product saved = repository.save(new Product(null, "Keyboard", new BigDecimal("79.99")));
System.out.println(saved.getId()); // 65f1c2a8e4b0a1d2c3e4f567

@Field — renaming and ordering

@Field controls the stored key name, which is useful when your Java property and the MongoDB field should differ. The document above stores name under product_name:

{
  "_id": "65f1c2a8e4b0a1d2c3e4f567",
  "product_name": "Keyboard",
  "price": 79.99
}

Indexes: @Indexed and @CompoundIndex

@Indexed declares a single-field index. Add unique = true to enforce uniqueness.

@Document("users")
public class User {
    @Id
    private String id;

    @Indexed(unique = true)
    private String email;

    private String city;
    private int age;
}

@CompoundIndex (placed on the class) defines a multi-field index. The def is a JSON key spec where 1 is ascending and -1 is descending.

@Document("users")
@CompoundIndex(name = "city_age_idx", def = "{'city': 1, 'age': -1}")
public class User {
    // fields as above
}

Warning: Index annotations only take effect when index creation runs. Enable spring.data.mongodb.auto-index-creation=true in development, or create indexes via a migration in production. See MongoDB Setup.

@DBRef vs embedded

You model related data either by embedding the child document inline or by referencing it. @DBRef stores a pointer to a document in another collection.

@Document("orders")
public class Order {
    @Id
    private String id;

    // Embedded — stored inline, read in one query:
    private List<LineItem> items;

    // Referenced — stored as a DBRef pointer, lazily resolved:
    @DBRef
    private Customer customer;
}

Embedding keeps the aggregate together and reads it in a single operation. @DBRef avoids duplicating large or frequently shared documents but adds an extra query per reference. The full tradeoff analysis lives in Embedded vs Referenced.

Tip: @DBRef is convenient but heavier than a plain reference. Many teams prefer storing just the referenced String id manually and looking it up explicitly when needed.

@Transient — exclude a field

Annotate a field with @Transient to keep it out of the stored document. It is useful for computed or injected values.

@Document("products")
public class Product {
    @Id private String id;
    private BigDecimal price;
    private int quantity;

    @Transient
    public BigDecimal getTotal() {     // computed, never persisted
        return price.multiply(BigDecimal.valueOf(quantity));
    }
}

Mapping a record

Records make immutable documents concise. Spring Data uses the canonical constructor to materialize them.

@Document("books")
public record Book(
        @Id String id,
        @Indexed(unique = true) String isbn,
        @Field("title") String name,
        BigDecimal price) { }

This produces clean, value-semantics documents with no boilerplate. See Java Records and Records as DTOs.

Last updated June 13, 2026
Was this helpful?