3. Relationship Mappings
1. @OneToOne
About
@OneToOnedefines a one-to-one relationship between two entity classes.It means each instance of Entity A is associated with exactly one instance of Entity B, and vice versa.
It is bidirectional or unidirectional based on how we model the relationship.
Example - Employee and EmployeeDetails
Each Employee has one EmployeeDetails record.
Each EmployeeDetails is linked to exactly one Employee.
Syntax
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
private EmployeeDetails details;
}This maps a simple unidirectional @OneToOne — Employee has a link to EmployeeDetails.
Attributes
mappedBy
Indicates the inverse side of the relationship (used in bidirectional mapping).
cascade
Defines cascade operations like persist, remove, merge etc.
fetch
Defines when to fetch related entity: EAGER (default) or LAZY.
optional
Whether the relationship is mandatory (false) or optional (true). Default is true.
orphanRemoval
Whether to automatically remove orphaned records.
How Relationships Are Stored in Database
Usually through a foreign key.
Typically one table has a foreign key reference to the other table.
For example:
1. Unidirectional @OneToOne
@OneToOneOnly one entity knows about the relationship.
@JoinColumn(name = "details_id", referencedColumnName = "id")details_idcolumn inemployeetable holds the foreign key referencingemployee_details(id).
cascade = CascadeType.ALLIf we persist or delete an Employee, EmployeeDetails will automatically be persisted or deleted too.
2. Bidirectional @OneToOne
@OneToOneBoth entities are aware of the relationship.
EmployeeDetailsowns the relationship (@JoinColumnis there).Employeejust references it throughmappedBy = "employee".mappedBytells JPA that the foreign key is managed by the other side (i.e., EmployeeDetails).
Cascade Types
We often want operations on the parent to affect the child entity too.
Typical cascade types with @OneToOne:
CascadeType.PERSIST: Save parent ➔ saves child.CascadeType.REMOVE: Delete parent ➔ deletes child.CascadeType.MERGE: Merge parent ➔ merges child.CascadeType.ALL: All operations (persist, merge, remove, refresh, detach).
FetchType
FetchType.EAGER(Default): Child entity is loaded immediately with parent entity.FetchType.LAZY: Child entity is loaded only when accessed.
In @OneToOne, default is EAGER (unlike @OneToMany where it’s LAZY).
2. @OneToMany
About
@OneToManydefines a one-to-many relationship between two entity classes.It means one instance of Entity A is associated with multiple instances of Entity B.
For example, one Department has many Employees.
Syntax
This is a unidirectional mapping — Department has a list of Employees.
Attributes
mappedBy
Specifies that this side is the inverse side (non-owning side).
cascade
Propagate operations (persist, merge, remove, etc.).
fetch
LAZY (default) or EAGER fetching behavior.
orphanRemoval
Automatically remove child records if they are no longer referenced.
How It Is Mapped in Database ?
Usually with a foreign key in the child table.
The
employeetable holds a foreign key (department_id) pointing todepartment(id).
1. Unidirectional @OneToMany
@OneToManyOnly one side (Department) knows about the relationship. But foreign key is still in child table (Employee).
@JoinColumn(name = "department_id")tells Hibernate to use department_id in the Employee table as a foreign key.Without
@JoinColumn, Hibernate would create an unnecessary join table (bad for performance if not needed).cascade = CascadeType.ALLwill propagate persist, remove, etc.
2. Bidirectional @OneToMany and @ManyToOne
@OneToMany and @ManyToOneBoth sides know the relationship.
Departmentis the inverse side (mappedBy = "department").Employeeowns the foreign key (@JoinColumn(name = "department_id")).orphanRemoval = truemeans if we remove an Employee from Department's employee list, Hibernate deletes it.
Common cascade types:
CascadeType.PERSIST: When saving Department, Employees are saved automatically.CascadeType.REMOVE: Deleting Department deletes Employees.CascadeType.MERGE: Merging Department merges Employees.CascadeType.ALL: All operations cascaded.
Fetch Type
FetchType.LAZY (default) -> Child entities (List<Employee>) are loaded only when accessed.
FetchType.EAGER -> Child entities are loaded immediately with parent entity.
@OneToManyis default LAZY.Be careful while changing it to EAGER — it can cause performance issues if there are lots of child records.
Orphan Removal
Setting
orphanRemoval = trueautomatically deletes Employees that are removed from the Department’s list.
Hibernate will DELETE that Employee from DB.
3. @ManyToOne
About
@ManyToOnedefines a many-to-one relationship between two entities.It means many instances of Entity A are associated with one instance of Entity B.
Example: Many Employees belong to one Department.
Syntax
This is a simple way to map Employee ➔ Department.
Attributes
fetch
Defines loading strategy: EAGER (default) or LAZY.
optional
Defines if the association is mandatory (false) or optional (true).
cascade
Defines cascading of operations (persist, merge, remove, etc.).
targetEntity
Defines the class type explicitly if necessary (rarely used).
How It Is Mapped in Database ?
The foreign key is created in the table of the owning entity (Employee).
employee.department_idpoints todepartment.id.
Entity Classes
@ManyToOneis placed on the field in the owning entity (Employee).@JoinColumn(name = "department_id")maps the foreign key column.fetch = FetchType.LAZYmeans Department will not be loaded until needed.optional = falsemeans the Employee must have a Department (null not allowed).
We can set cascade operations:
But careful — usually we don’t cascade from child to parent! (We don't want saving Employee to randomly save/update Department.)
Best Practice: Avoid cascade for @ManyToOne unless there is a strong reason.
Fetch Type
FetchType.EAGER (default) -> Always loads the Department with the Employee.
FetchType.LAZY -> Loads the Department only when accessed.
Note:
In
@ManyToOne, default is EAGER (unlike@OneToManywhere default is LAZY).It is recommended to manually set it to
LAZYfor large data models to avoid performance issues.
Optional Attribute
optional = true (default) -> Employee can exist without a Department (foreign key can be null).
optional = false -> Department is required for Employee (foreign key must not be null).
If we set optional = false, Hibernate generates NOT NULL constraint on department_id in employee table.
4. @ManyToMany
About
@ManyToManydefines a many-to-many relationship between two entities.It means many instances of Entity A are related to many instances of Entity B.
Example: Many Employees can work on many Projects.
Syntax
This creates a many-to-many relation between Student and Course
Attributes
fetch
Defines loading strategy: LAZY (default) or EAGER.
cascade
Defines cascade operations on associated entities.
targetEntity
Explicitly defines associated class if needed.
How It Is Mapped in Database ?
JPA creates an intermediate join table to manage the association.
@ManyToMany always requires an intermediate join table because relational databases do not support direct many-to-many relations.
The join table holds two foreign keys:
One pointing to Entity A.
One pointing to Entity B.
The join table can optionally have a composite primary key (both foreign keys together).
1. Unidirectional @ManyToMany Mapping
@ManyToMany MappingOne Entity knows about the other.
The association is one-way.
Only the owning entity manages the relationship.
Other entity (target entity) does NOT know about the relation.
Generated Database Tables
student
id, name
id
-
course
id, title
id
-
student_course
student_id, course_id
(student_id, course_id) composite key
FK (student_id) → student(id) FK (course_id) → course(id)
Join Table (student_course) is fully managed by Student.
Course does not know anything about Students.
Important
Since
Coursedoes not have any@ManyToMany(mappedBy = ...), it is unaware of this relationship in Java model.
2. Bidirectional @ManyToMany Mapping
@ManyToMany MappingBoth Entities know about each other.
Association is two-way.
One side is the owning side (
@JoinTable), and the other is the inverse side (mappedBy).Only the owning side actually updates the join table in database.
In Java memory, both sides need to be manually synchronized.
Generated Database Tables
student
id, name
id
-
course
id, title
id
-
student_course
student_id, course_id
(student_id, course_id) composite key
FK (student_id) → student(id) FK (course_id) → course(id)
Same join table structure as unidirectional.
But now, both entities are aware of the relationship.
Hibernate still only uses the owning side (
Student) to insert/update/delete rows in the join table.
Fetch Type
FetchType.LAZY (default) -> Load related entities when accessed.
FetchType.EAGER -> Load related entities immediately.
Best Practice:
Keep ManyToMany as LAZY.
EAGER loading on large many-to-many relations can explode query size and cause serious performance problems.
Cascade operations
But in real-world apps:
Cascade cautiously.
Usually, you don’t cascade on
@ManyToManybecause both sides can exist independently.
Owning Side vs Inverse Side
Owning Side -> The entity that defines @JoinTable or @JoinColumn.
Inverse Side -> The entity with mappedBy.
Only the owning side is responsible for updating the join table.
The inverse side just reflects the relationship.
5. @JoinColumn
About
@JoinColumnis used to customize the foreign key column generated for an entity association (like@OneToOne,@ManyToOne, etc.).It defines how two tables are joined in the database by explicitly specifying:
The foreign key column name.
Referenced primary key column.
Without
@JoinColumn, JPA uses default naming conventions (which can be confusing or inconsistent).
Where is @JoinColumn Used ?
@JoinColumn Used ?It is placed on the owning side of relationships:
@OneToOne@ManyToOne(Optionally) in
@OneToMany+@JoinColumn(to avoid a join table)
Also used in
@JoinTable(for join table’s joinColumns and inverseJoinColumns).
Syntax of @JoinColumn
Composite Foreign Keys (Multiple @JoinColumns)
If we need multiple columns (composite foreign key), use @JoinColumns:
Important Parameters
name
Name of the foreign key column in the owner’s table.
referencedColumnName
Name of the primary key column in the target entity. Default: "id".
nullable
Whether the foreign key can be NULL (default: true).
unique
Whether the foreign key must be UNIQUE.
insertable
Whether the foreign key column is included in SQL INSERT (default: true).
updatable
Whether the foreign key column is included in SQL UPDATE (default: true).
foreignKey
Specifies foreign key constraint name in database.
Example — @ManyToOne with @JoinColumn
@ManyToOne with @JoinColumnDatabase Tables Generated:
department
id (PK), department_name
employee
id (PK), name, department_id (FK to department.id)
@JoinColumn(name = "department_id")tells JPA to create a foreign key column named department_id in Employee table.It references the id column of Department table.
If @JoinColumn is not given explicitly:
Hibernate defaults to
<association_property>_<referenced_primary_key>.In above example:
department_idwould still happen automatically — but being explicit is better for clarity and control.
How @JoinColumn behaves if missing ?
@JoinColumn behaves if missing ?JPA will:
Generate a foreign key.
Guess a column name using entity field name and primary key.
Constraint name will be auto-generated.
It is considered good practice to always specify @JoinColumn manually for:
Database readability.
Easier migrations.
Predictable schema.
6. @JoinTable
About
@JoinTableis used to define the Join Table (i.e., intermediate table) when two entities have a many-to-many or sometimes one-to-many association.A Join Table is a separate table that holds foreign keys referencing the primary keys of the two associated tables.
@JoinTablegives you full control over:The name of the intermediate table.
The join columns (owner side foreign key).
The inverse join columns (other side foreign key).
Where @JoinTable is Used ?
@JoinTable is Used ?Mostly in
@ManyToManymappings.Sometimes in
@OneToMany(only if you want to manage the relation via a separate table instead of a foreign key directly).
Syntax of @JoinTable
Parameters
name
Name of the join table in DB.
joinColumns
Specifies the column that refers to the current (owning) entity’s primary key.
inverseJoinColumns
Specifies the column that refers to the other (inverse) entity’s primary key.
uniqueConstraints
Optional. Defines uniqueness constraints on join table columns.
indexes
Optional. Allows creating indexes on join table columns.
Example — @ManyToMany using @JoinTable
@ManyToMany using @JoinTableSuppose we have two entities:
Employee
Project
An employee can work on multiple projects, and a project can have multiple employees.
Entity Classes:
Database Structure Generated
employee
id (PK), name
project
id (PK), project_name
employee_project
employee_id (FK to employee.id), project_id (FK to project.id)
The employee_project table acts as a bridge table — no direct foreign key inside employee or project tables.
Ownership in @JoinTable
The entity where
@JoinTableis defined is the owning side.The other entity is inverse side (no control over join table).
In example above:
Employee owns the relationship.
Project does not.
Last updated