skip to content

What is @DBRef in Spring Data MongoDB, how does it behave at runtime, and what are its drawbacks versus embedding or manual references?

level: middleimportance: should knowfreq 55%

answer

  1. DBRef = { $ref, $id } wrapper, not embedded data
  2. eager by default -> N+1; lazy = proxy
  3. no cascade, no referential integrity
  4. @DocumentReference is the modern preferred alternative
  5. 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
java
@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

for a junior

Know @DBRef links to another collection rather than embedding.

for a middle

Know eager-vs-lazy resolution, no-cascade, and the N+1 risk.

for a senior

Compare @DBRef vs @DocumentReference vs manual refs vs embedding and justify per aggregate.

for a principal

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.

context