JPA offers four association mappings — @ManyToOne, @OneToMany, @OneToOne and @ManyToMany. Explain what each expresses, and how you would choose between them for a model of orders, customers and tags.
answer
- Read the name outward from the annotated field
- @ManyToOne = the real FK column
- mappedBy side is a view, not a second relationship
- @ManyToMany dies when the link gains attributes
- Direction is a Java choice; cardinality is a data fact
basics
~20 sThey declare cardinality from the annotated field outward. @ManyToOne: many rows reference one (Order to Customer) and holds the foreign key. @OneToMany: the inverse collection (Customer to Orders). @OneToOne: at most one each way. @ManyToMany: many both ways, via a link table.
solid answer
~50 sThe annotation names the cardinality **from the annotated field outward**. `@ManyToOne` on `Order.customer` means many orders reference one customer; this side maps directly onto the foreign-key column, so it is the cheapest and most fundamental mapping. `@OneToMany` on `Customer.orders` is the collection view of that same relationship; in a bidirectional pair it is declared `mappedBy = "customer"` and generates no SQL of its own. `@OneToOne` means at most one row on each side (`Order` to `Invoice`); one table carries the FK, typically with a unique constraint, or the two share a primary key via `@MapsId`. `@ManyToMany` means both sides may reference many of the other (`Order` to `Tag`) and needs a separate link table. Practical rule: always map the `@ManyToOne` — it is the one the schema actually has. Add the `@OneToMany` inverse only if code really navigates parent to children, and use `@ManyToMany` only while the link itself carries no data.
code
java · 25 lines@Entity
class Order {
@Id @GeneratedValue Long id;
@ManyToOne
@JoinColumn(name = "customer_id")
Customer customer; // many orders -> one customer (owns the FK)
@OneToOne(mappedBy = "order")
Invoice invoice; // invoice table holds the FK
@ManyToMany
@JoinTable(name = "order_tag",
joinColumns = @JoinColumn(name = "order_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id"))
Set<Tag> tags;
}
@Entity
class Customer {
@Id @GeneratedValue Long id;
@OneToMany(mappedBy = "customer")
Set<Order> orders; // inverse view of Order.customer
}go deeper
Name the four annotations, say which side has the foreign key, and give one concrete example of each from an everyday domain.
Add that a bidirectional pair maps one column, that mappedBy marks the non-owning view, and that direction is independent of cardinality.
Argue from the schema outward, flag @ManyToMany as a temporary shape that dies when the link gains attributes, and treat unneeded collections as a cost.
Frame associations as aggregate-boundary decisions: which navigations the domain guarantees, which links should be entities in their own right, and where an association should be replaced by an id reference or a query.
## What an association mapping is A relational database expresses relationships with foreign-key columns and, where needed, link tables. An object model expresses them with references and collections. A JPA association annotation is the declaration that tells the ORM how one maps onto the other: which field corresponds to which column, and how many rows may sit on each end. The annotation name is read **from the annotated field outward**: the first word is the multiplicity of the side you are standing on, the second the multiplicity of the side you are pointing at. `@ManyToOne List<...>` is therefore nonsense, and `@OneToMany Customer` equally so — the plural half of the name must line up with the collection. ## The four kinds **@ManyToOne** — many entities of this type reference one of the target. `Order.customer` is the archetype. This is the only association that maps one-for-one onto a plain foreign-key column on the annotated entity's own table, which makes it the most important of the four: if you map nothing else, map this. **@OneToMany** — the reverse view: one entity holds a collection of many. `Customer.orders` is the archetype. Crucially, in a bidirectional pair the `@OneToMany` describes the *same* database column as the matching `@ManyToOne`; it is not an extra relationship. It is declared with `mappedBy` naming the field on the other side that owns the column. **@OneToOne** — at most one row on each side: `Order` to `Invoice`, `User` to `UserProfile`. Relationally this is a foreign key on one of the two tables plus a unique constraint, or a shared primary key where the dependent table's PK is also an FK to the parent (expressed with `@MapsId`). The side holding the column is the owner; the other side uses `mappedBy`. **@ManyToMany** — both ends may reference many of the other: orders and tags, users and roles. There is no column that can hold this, so a third table of `(order_id, tag_id)` pairs is required. One side is the owner and the other, if mapped at all, uses `mappedBy`. ## Direction is separate from cardinality Cardinality is a property of the data. **Direction** is a property of your Java model: whether you mapped one side, or both. `@ManyToOne` alone gives a unidirectional many-to-one. Adding a `mappedBy` `@OneToMany` makes the same relationship bidirectional — the schema does not change at all. Direction is therefore free to choose per use case, while cardinality is dictated by the domain. ## Choosing in practice Start from the schema, not from the prose of the requirement. Ask: "which table would hold the foreign key?" That table's entity gets the `@ManyToOne` (or the owning `@OneToOne`). Then ask whether any code path genuinely needs to navigate the other way; only then add the inverse collection. Unneeded collections are pure liability — they are loaded, cascaded, and reasoned about forever, and a large `@OneToMany` is a well-known source of memory and query problems. `@ManyToMany` deserves particular caution. It is correct only while the association carries no attributes of its own. The moment the business wants "when was this tag added, and by whom", the link table needs columns, and a `@ManyToMany` cannot express them. The standard move is to promote the link table into a first-class entity (`OrderTag`) with two `@ManyToOne` associations, converting one many-to-many into two many-to-ones. Many teams do this pre-emptively because the promotion later is a breaking model change. `@OneToOne` is also worth questioning: if the two tables always exist together and are always loaded together, the relationship may be a sign that the two tables should be one, or that the second is really an `@Embeddable` component. Legitimate uses are optional detail rows, rarely-loaded large payloads, and table-splitting for storage reasons. ## What the annotations do not decide The kind of association does not, by itself, decide the join strategy, the column names, or the write behaviour. Those come from `@JoinColumn`/`@JoinTable`, from `mappedBy` (which end owns the write), and from `cascade`/`orphanRemoval`. Cardinality is only the first of several independent decisions.
- Does adding the @OneToMany inverse side change the database schema?No. A bidirectional pair describes one and the same foreign-key column; the `mappedBy` side is purely a Java navigation path. The schema for `Order.customer` alone and for `Order.customer` plus `Customer.orders` is identical. Direction is a modelling choice with no relational cost — its costs are in loading and lifecycle behaviour instead.
- When would you replace a @ManyToMany with two @ManyToOne associations?As soon as the link itself needs attributes — an added-at timestamp, a quantity, a role, a soft-delete flag — because a `@ManyToMany` link table can only hold the two keys. You introduce an explicit link entity with its own identifier and two `@ManyToOne` fields. It also gives you finer control over writes, since each link row becomes an ordinary managed entity.
Cardinality is how the rooms of a building actually connect; direction is which doors you bothered to cut. The wall is the same either way.
saying these in an interview costs you the question
- Saying @OneToMany and @ManyToOne are two different relationships that must both be persisted
- Reading the annotation name backwards, e.g. putting @ManyToOne on the collection
- Assuming @ManyToMany can carry extra columns such as a quantity or timestamp
- Claiming a bidirectional mapping requires an extra column or table
- Modelling every relationship in both directions by default