What is @DBRef in Spring Data MongoDB, how does it behave at runtime, and what are its drawbacks versus embedding or manual references?
answer
- DBRef = { $ref, $id } wrapper, not embedded data
- eager by default -> N+1; lazy = proxy
- no cascade, no referential integrity
- @DocumentReference is the modern preferred alternative
- embed owned data, reference shared/large data
basics
~20 s@DBRef stores a link to a document in another collection instead of embedding it. On load, Spring fetches the referenced document with a separate query. It has no cascade and can cause many extra reads (N+1).
solid answer
~50 s@DBRef marks a property as a reference to a document in another (or the same) collection rather than an embedded sub-document. In storage it is a DBRef object holding the target collection name and the referenced _id ($ref/$id). When you load the owning document, Spring Data resolves the reference with additional queries — eagerly by default, or lazily via @DBRef(lazy = true) using a proxy. Drawbacks: no referential integrity enforced by MongoDB, no cascade on save/delete (you must persist the referenced entity yourself), and loading collections of @DBRefs triggers many round-trips (N+1). Modern Spring Data (3.3+) offers @DocumentReference as a more flexible alternative that stores just the id (or a custom lookup) without the DBRef wrapper. Best practice: prefer embedding for owned data, and manual references or @DocumentReference over @DBRef for large or independent aggregates.
code
java · 24 lines@Document("orders")
public class Order {
@Id
private String id;
// Eager by default: loading an Order triggers a query for the Customer.
@DBRef(lazy = true)
private Customer customer; // stored as { $ref: "customers", $id: <customerId> }
// Newer, preferred style — stores just the id, customizable lookup:
// @DocumentReference(collection = "customers")
// private Customer customer;
}
@Document("customers")
public class Customer {
@Id private String id;
private String name;
}
// NOTE: no cascade — you must save the Customer yourself before/independently of the Order.
// customerRepo.save(customer);
// orderRepo.save(order);go deeper
Know @DBRef links to another collection rather than embedding.
Know eager-vs-lazy resolution, no-cascade, and the N+1 risk.
Compare @DBRef vs @DocumentReference vs manual refs vs embedding and justify per aggregate.
Reason about data-modeling trade-offs at scale: aggregate boundaries, $lookup, read amplification, and avoiding cross-collection joins in a document store.
MongoDB is document-oriented and favors **embedding** related data inside one document. But sometimes you want a **reference** to a document living in another collection (a many-to-one or shared entity). Spring Data MongoDB supports this with **@DBRef** (`org.springframework.data.mongodb.core.mapping.DBRef`). **How it is stored:** a `@DBRef` property is written as a BSON **DBRef** — a special sub-object of the form `{ "$ref": "<collection>", "$id": <referencedId> }` (optionally `$db`). It stores the target collection and the referenced document's `_id`; it does **not** copy the referenced document's fields. **How it is read:** when the `MappingMongoConverter` reads the owning document, it sees the DBRef and resolves it: - **Eager (default):** it immediately issues a `find` for the referenced document and populates the property. A `List<@DBRef T>` of N references can cause up to N extra queries — a classic **N+1** problem. - **Lazy:** `@DBRef(lazy = true)` installs a lazy-loading **proxy**; the referenced document is fetched on first access. This helps latency but can throw or hit the DB at surprising times (e.g. during serialization or when the connection/session is gone). **Critical limitations:** 1. **No cascade.** Saving the parent does **not** save the referenced entity, and deleting the parent does **not** delete the child. You must save/delete referenced documents explicitly. There is no JPA-style `cascade`. 2. **No referential integrity.** MongoDB does not enforce that the target exists; a dangling DBRef simply resolves to null/error. 3. **Performance.** Extra round-trips per reference; you cannot easily do server-side joins over DBRefs (`$lookup` doesn't natively follow DBRef wrapper objects cleanly). 4. **Cross-database refs** via `$db` are discouraged and poorly supported by drivers. **@DocumentReference (Spring Data MongoDB 3.3+):** a newer alternative (`org.springframework.data.mongodb.core.mapping.DocumentReference`). It stores just the referenced id (or any value/SpEL-defined lookup) as a plain field — no `$ref`/`$id` wrapper — and lets you customize the `lookup` query and `collection`. It supports lazy loading and is generally preferred over `@DBRef` because it is more flexible and produces cleaner documents that play well with `$lookup` aggregations. **When to use which:** - **Embed** when the child is owned by and only accessed through the parent, is bounded in size, and changes with the parent (e.g. order line items). - **Reference** (via `@DocumentReference` preferably, or a manual `String`/`ObjectId` id field you resolve yourself) when the entity is large, shared across many parents, independently queried, or unbounded (e.g. a User referenced by many Orders). - **@DBRef** is legacy-friendly but avoid it for new designs due to N+1 and no-cascade footguns. **Gotcha:** because there is no cascade, a common bug is saving a parent that holds a transient (never-saved) referenced object — the reference points to an id that doesn't exist yet, or the driver errors on a null id. Always persist referenced aggregates first.
- Does saving a parent with a @DBRef also save the referenced document?No. There is no cascade in Spring Data MongoDB. You must save (or delete) the referenced document explicitly; the parent only stores the reference wrapper.
- Why might a team migrate from @DBRef to @DocumentReference?@DocumentReference stores a plain id (no $ref/$id wrapper), allows a custom lookup query and collection, produces cleaner documents that work with $lookup aggregations, and still supports lazy loading — avoiding DBRef's rigidity.
saying these in an interview costs you the question
- Assuming @DBRef cascades saves/deletes like JPA cascade.
- Thinking @DBRef embeds the referenced document's fields.
- Believing MongoDB enforces referential integrity for DBRefs.
- Not recognizing the N+1 problem when loading a list of @DBRefs.