hibernate-007: Bidirectional One-to-Many & Many-to-One in Hibernate (Department ↔ Employee)

Hunor Vadasz-Perhat - Feb 10 - - Dev Community

Bidirectional One-to-Many & Many-to-One in Hibernate (Department ↔ Employee)


🚀 1️⃣ Bidirectional One-to-Many & Many-to-One

✅ In a bidirectional relationship:

  • The "many" side (Employee) owns the relationship (because it holds the foreign key).
  • The "one" side (Department) is the inverse side and uses mappedBy to refer to the owning side.

📌 Step 1: @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 column in Employee table
    private Department department;
}
Enter fullscreen mode Exit fullscreen mode

✅ The Employee table gets a department_id foreign key.


📌 Step 2: @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's `department` field
    private List<Employee> employees = new ArrayList<>();
}
Enter fullscreen mode Exit fullscreen mode

mappedBy = "department" tells Hibernate:

  • "The foreign key is already in Employee.department."
  • "Do not create an extra join table."

🔥 Generated SQL (No 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)
);
Enter fullscreen mode Exit fullscreen mode

Correct design! The foreign key is stored in Employee.department_id, and no join table is created.


🚀 2️⃣ How to Use the Bidirectional Relationship in Java

📌 Saving Data

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);

List<Employee> employees = new ArrayList<>();
employees.add(emp1);
employees.add(emp2);

department.setEmployees(employees); // ✅ Set reference in Department

entityManager.persist(department);
entityManager.persist(emp1);
entityManager.persist(emp2);
Enter fullscreen mode Exit fullscreen mode

Both Department and Employee are linked properly.


📌 Querying the Relationship

Get Employees for a Department

Department dept = entityManager.find(Department.class, 1L);
List<Employee> employees = dept.getEmployees();
employees.forEach(emp -> System.out.println(emp.getName()));
Enter fullscreen mode Exit fullscreen mode

Get Department for an Employee

Employee emp = entityManager.find(Employee.class, 10L);
System.out.println(emp.getDepartment().getName());
Enter fullscreen mode Exit fullscreen mode

Both queries work because the relationship is bidirectional.


🚀 3️⃣ Summary: Unidirectional vs. Bidirectional

Feature Unidirectional @OneToMany Unidirectional @ManyToOne Bidirectional (@OneToMany + @ManyToOne)
@OneToMany used? ✅ Yes (But Creates Extra Table) ❌ No ✅ Yes (mappedBy prevents join table)
@ManyToOne used? ❌ No ✅ Yes (Owning Side) ✅ Yes (Owning Side)
Uses @JoinColumn? ❌ No ✅ Yes ✅ Yes (In @ManyToOne)
Foreign key in Employee table? ❌ No ✅ Yes ✅ Yes
Extra join table? ✅ Yes (Unwanted) ❌ No ❌ No

Best Practice: Use Bidirectional @OneToMany + @ManyToOne


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .