🚀 Bidirectional One-to-Many & Many-to-One in Hibernate
Now, let’s fully explore bidirectional One-to-Many and Many-to-One relationships with real examples, database schema, and queries.
📌 1️⃣ What is a Bidirectional One-to-Many & Many-to-One?
- One parent entity has multiple child entities (One-to-Many).
- Each child entity belongs to one parent entity (Many-to-One).
-
The "Many" side owns the foreign key, and the "One" side is mapped using
mappedBy
.
✅ Best Practice
-
The child (
Many
) owns the relationship (@ManyToOne
with@JoinColumn
). -
The parent (
One
) just references it (@OneToMany(mappedBy = "field")
).
📌 2️⃣ Example: Department ↔ Employee
- One
Department
has manyEmployees
(@OneToMany
). - Each
Employee
belongs to oneDepartment
(@ManyToOne
).
✅ Step 1: Define @ManyToOne
(Owning Side - Employee
)
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "department_id") // ✅ Foreign key in Employee table
private Department department;
}
✅ The department_id
foreign key is stored in Employee
.
✅ Step 2: Define @OneToMany(mappedBy = "department")
(Inverse Side - Department
)
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department") // ✅ Refers to Employee.department
private List<Employee> employees = new ArrayList<>();
}
✅ mappedBy = "department"
tells Hibernate:
- "The foreign key is already in
Employee.department_id
." - "Don’t create an extra join table."
📌 3️⃣ Database Schema (No Extra Join Table!)
CREATE TABLE department (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
CREATE TABLE employee (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
department_id BIGINT, -- ✅ Foreign key column
FOREIGN KEY (department_id) REFERENCES department(id)
);
✅ The foreign key is only in Employee.department_id
, keeping the relationship correct.
📌 4️⃣ Saving Data in Hibernate
Department department = new Department();
department.setName("IT Department");
Employee emp1 = new Employee();
emp1.setName("Alice");
emp1.setDepartment(department); // ✅ Set reference in Employee
Employee emp2 = new Employee();
emp2.setName("Bob");
emp2.setDepartment(department);
department.getEmployees().add(emp1); // ✅ Set reference in Department
department.getEmployees().add(emp2);
entityManager.persist(department);
entityManager.persist(emp1);
entityManager.persist(emp2);
🚀 Now both Department
and Employee
are correctly linked!
📌 5️⃣ Querying Both Directions
✅ Get Employees from Department
Department dept = entityManager.find(Department.class, 1L);
List<Employee> employees = dept.getEmployees();
employees.forEach(emp -> System.out.println(emp.getName())); // ✅ Works!
✅ Get Department from Employee
Employee emp = entityManager.find(Employee.class, 10L);
System.out.println(emp.getDepartment().getName()); // ✅ Works!
✅ Both queries work because the relationship is bidirectional.
📌 6️⃣ Summary: One-to-Many & Many-to-One
Feature | One-to-Many (Department ) |
Many-to-One (Employee ) |
---|---|---|
@OneToMany(mappedBy = "department") |
✅ Yes | ❌ No |
@ManyToOne used? |
❌ No | ✅ Yes |
@JoinColumn(name = "fk_column") |
❌ No | ✅ Yes |
Foreign key location? | ❌ Not in Department
|
✅ In Employee.department_id
|
Extra join table? | ❌ No | ❌ No |
Reference back? | ✅ Yes | ✅ Yes |
✅ Best Practice: Use bidirectional @OneToMany
+ @ManyToOne
to avoid unnecessary join tables and keep data consistent.
Happy coding! 🚀