@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 (Product → product).
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:
@Idhere isorg.springframework.data.annotation.Id, notjakarta.persistence.Id. Importing the JPA one is a common mistake that leaves your_idunmapped.
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 type | Stored as | Notes |
|---|---|---|
String | ObjectId (or string) | Most common; auto-generated when null |
ObjectId | ObjectId | Use the driver’s org.bson.types.ObjectId directly |
Long / Integer | numeric _id | You 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=truein 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:
@DBRefis convenient but heavier than a plain reference. Many teams prefer storing just the referencedStringid 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.