Mapping and Relationships
What is the owning side of a relationship and what does mappedBy mean?
The owning side is the entity whose table holds the foreign key; only changes on the owning side are written to the database. The other side uses mappedBy to say "this is just a mirror of the field over there". In @OneToMany/@ManyToOne, the @ManyToOne side always owns.
@Entity
public class Order {
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
public void addItem(OrderItem item) { // keep both sides in sync
items.add(item);
item.setOrder(this);
}
}
@Entity
public class OrderItem {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id") // owning side: holds the FK
private Order order;
}Note: If you only do order.getItems().add(item) without item.setOrder(order), the order_id column is saved as NULL.
Unidirectional @OneToMany vs bidirectional: which is better?
A unidirectional @OneToMany without @JoinColumn creates an extra join table (orders_items) and produces extra INSERT/DELETE statements. With @JoinColumn it uses a FK but may still issue extra UPDATEs to set it.
The most efficient mapping is @ManyToOne on the child (often enough on its own) plus, if you need navigation from the parent, @OneToMany(mappedBy = ...).
@JoinColumn vs @JoinTable?
@JoinColumn names the foreign-key column in the owning table (order_items.order_id). @JoinTable describes a link table with two FKs; it is required for @ManyToMany and optional for others.
@ManyToMany
@JoinTable(name = "product_tags",
joinColumns = @JoinColumn(name = "product_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id"))
private Set<Tag> tags = new HashSet<>();What are the problems with @ManyToMany and how do you avoid them?
- Use a
Set, not aList: with a List (a "bag") Hibernate deletes all link rows and re-inserts them when one element is removed. - Never use
CascadeType.REMOVE/ALLon it: deleting a product would delete shared tags. - If the link needs extra columns (quantity, added_at), replace it with a join entity and two
@ManyToOnes, e.g.CartItem(user, product, quantity).
Explain cascade types. What is orphanRemoval and how is it different from CascadeType.REMOVE?
Cascade passes an operation from parent to child: PERSIST, MERGE, REMOVE, REFRESH, DETACH, or ALL. Use it on parent → child compositions (Order → OrderItem), not on @ManyToOne (an item must not delete its product).
| CascadeType.REMOVE | orphanRemoval = true | |
|---|---|---|
| Parent deleted | children deleted | children deleted |
| Child removed from the collection | nothing happens (child row stays) | child row is deleted |
Inheritance mapping: SINGLE_TABLE vs JOINED vs TABLE_PER_CLASS vs @MappedSuperclass?
| Strategy | Tables | Pros / cons |
|---|---|---|
| SINGLE_TABLE (default) | one table + discriminator column | fastest, no joins; subclass columns must be nullable |
| JOINED | parent table + one table per subclass | normalized, NOT NULL possible; joins on every read |
| TABLE_PER_CLASS | one full table per concrete class | polymorphic queries need UNION; rarely used |
| @MappedSuperclass | no table for the parent | shares fields only (id, createdAt); cannot query the parent type |
How should you map an enum, and what are embeddables?
Use @Enumerated(EnumType.STRING). The default ORDINAL stores 0, 1, 2..., so inserting a new constant in the middle silently corrupts existing data.
@Embeddable is a value object without its own identity or table; its fields are stored as columns of the owning entity (@Embedded Address address → street, city, pincode columns).
@Enumerated(EnumType.STRING)
private OrderStatus status; // stored as 'PLACED', 'SHIPPED'
@Embedded
private Address shippingAddress;What are the default fetch types for each association?
| Annotation | Default fetch |
|---|---|
@ManyToOne | EAGER |
@OneToOne | EAGER |
@OneToMany | LAZY |
@ManyToMany | LAZY |
@ElementCollection | LAZY |
Tip: Set fetch = FetchType.LAZY on every @ManyToOne and @OneToOne, then fetch what each use case needs explicitly.