Owning vs Inverse Side (mappedBy)
Owning vs Inverse Side (mappedBy)
When you model a bidirectional association in JPA — for example, a Customer that holds a list of Order objects, and each Order that holds a reference back to its Customer — Hibernate must know which of the two Java fields corresponds to the actual foreign key column in the database. That is the single question answered by the owning side / inverse side distinction, and getting it wrong is one of the most common reasons developers see association changes silently ignored at flush time.
The Core Rule
In every bidirectional relationship there are exactly two sides:
- The owning side — the field that carries the physical foreign key column. Hibernate reads this field when it decides what to write to the database. This side is annotated with
@JoinColumn(for@ManyToOne/@OneToOne) or@JoinTable(for@ManyToMany). - The inverse side — the "mirror" field on the other entity. It exists only for navigating the Java object graph. Hibernate ignores changes made exclusively to the inverse side when writing to the database. This side is declared with the
mappedByattribute.
mappedBy = "fieldName" says "the foreign key for this relationship is managed by the field called fieldName on the other entity — I am just a read-only mirror."
Bidirectional @ManyToOne / @OneToMany Example
This is by far the most common bidirectional relationship. A Customer has many Order objects; each Order belongs to one Customer. The foreign key (customer_id) lives in the orders table, so the Order.customer field is the owning side.
What Happens If You Only Set the Inverse Side
This is the classic trap. A developer adds an order to the customer's list but forgets to set the back-reference on the order:
Because customer.orders is the inverse side (it has mappedBy), Hibernate simply does not inspect it when generating SQL. The fix is always to set both sides:
Bidirectional @OneToOne
The same rule applies. Pick one entity to hold the FK column and mark the other with mappedBy:
Bidirectional @ManyToMany
In a many-to-many association, the FK lives in a join table. Only one entity needs to define the join table with @JoinTable; the other uses mappedBy:
course.students alone will never insert a row into the student_course join table because course.students is the inverse side. Always add the course to the student's set (or use a bidirectional helper method) so Hibernate sees the change on the owning side.
Choosing Which Side Owns the FK
For @ManyToOne / @OneToMany the choice is natural: the FK column is always on the "many" side of the table, so the @ManyToOne field in the "many" entity is always the owning side. You cannot reverse this without restructuring your schema.
For @OneToOne and @ManyToMany you have more freedom. A common convention is to place the owning side on the entity that is the more "dependent" or "child" one — the one that cannot exist without the other. This way the FK or join table naturally lives next to the data that depends on it.
Quick Reference: mappedBy Rules
- Exactly one side in a bidirectional pair uses
mappedBy; the other side owns the FK. - The value of
mappedByis the field name on the owning entity, not the column name. - The side with
mappedBymust not also have@JoinColumnor@JoinTable— those go on the owning side. - Changes made only to the inverse side (
mappedBy) are silently ignored by Hibernate at flush time. - Always synchronise both sides in memory, even if only the owning side drives the SQL — stale in-memory state leads to hard-to-debug second-level cache or within-transaction read bugs.
Summary
The owning side is the field with the foreign key column (annotated with @JoinColumn or @JoinTable). The inverse side is declared with mappedBy and is a pure Java navigation convenience — Hibernate writes nothing to the database based on it. Understanding this distinction prevents the most common JPA pitfall: updating only the inverse side and seeing nothing change in the database. Use helper methods that synchronise both sides together, and you will never be bitten by this again.