Hibernate Tutorial

Core Hibernate

Hibernate Mapping

Hibernate Annotations

Hibernate with Spring Framework

Hibernate with Database

Hibernate Log4j

Inheritance Mapping

Hibernate - Many-to-Many Mapping

Many-to-Many mappings are quite common in database design and Hibernate offers a way to represent this relation at the object level. In this tutorial, we'll dive into how to map a many-to-many relationship using Hibernate.

Example Scenario:

Let's say we have Student and Course entities. A student can enroll in multiple courses, and a course can have multiple students.

1. Database Tables:

  • student table: To store student information.
  • course table: To store course details.
  • student_course table: A join table to map the many-to-many relationship.

2. Setting Up Dependencies:

Ensure you have the necessary dependencies for Hibernate in your project. If you're using Maven, you'd typically include hibernate-core among others.

3. Mapping Entities:

Student.java:

@Entity
@Table(name="student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="student_id")
    private int id;

    @Column(name="first_name")
    private String firstName;

    @Column(name="last_name")
    private String lastName;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses;

    // constructors, getters, setters...
}

Course.java:

@Entity
@Table(name="course")
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="course_id")
    private int id;

    @Column(name="title")
    private String title;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "course_id"),
        inverseJoinColumns = @JoinColumn(name = "student_id")
    )
    private Set<Student> students;

    // constructors, getters, setters...
}

Explanation:

  • @ManyToMany: This annotation indicates a many-to-many relationship.
  • @JoinTable: This specifies the join table (or bridge table).
    • name: Name of the join table.
    • joinColumns: The column(s) representing the current entity.
    • inverseJoinColumns: The column(s) representing the opposite side entity.

4. Working with Data:

Saving:

Session session = sessionFactory.getCurrentSession();
session.beginTransaction();

// create courses and students
Course tempCourse1 = new Course("Maths");
Course tempCourse2 = new Course("Physics");

Student tempStudent1 = new Student("John", "Doe");
Student tempStudent2 = new Student("Mary", "Public");

// add courses for students
tempStudent1.addCourse(tempCourse1);
tempStudent1.addCourse(tempCourse2);
tempStudent2.addCourse(tempCourse1);

// save the students (and this will save the courses because of CascadeType.ALL)
session.save(tempStudent1);
session.save(tempStudent2);

session.getTransaction().commit();

Retrieving:

Session session = sessionFactory.getCurrentSession();
session.beginTransaction();

// fetch student with id=1
Student tempStudent = session.get(Student.class, 1);

// print courses for that student
System.out.println(tempStudent.getCourses());

session.getTransaction().commit();

5. FetchType and CascadeType:

  • FetchType.LAZY: This ensures that the related entities are not loaded unless they're accessed. It's a good practice to use LAZY fetch type for many-to-many mappings to avoid loading everything when it's not needed.

  • CascadeType.ALL: This means operations like save, delete, etc., will cascade to the associated entities. If you save a student, its courses get saved, and vice versa.

Conclusion:

Mapping many-to-many relationships can be tricky, especially with the need to have a join table. However, Hibernate simplifies the process and abstracts the complexities, making it easy to work with such relationships at the object level. Ensure you understand cascading and fetching strategies to use them effectively in different scenarios.

  1. Configuring many-to-many associations in Hibernate:

    • In Hibernate, many-to-many mapping is used to represent a relationship between two entities where each entity can be associated with multiple instances of the other.
    • Configuration involves defining the many-to-many element in the Hibernate XML mapping file or using annotations.
    <!-- XML Mapping -->
    <many-to-many name="students" class="com.example.Student">
       <join-table name="student_course">
          <key column="course_id" />
          <inverse-key column="student_id" />
       </join-table>
    </many-to-many>
    
    // Annotation Mapping
    @ManyToMany
    @JoinTable(
       name = "student_course",
       joinColumns = @JoinColumn(name = "course_id"),
       inverseJoinColumns = @JoinColumn(name = "student_id"))
    private Set<Student> students;
    
  2. Mapping many-to-many relationships with Hibernate annotations:

    • Many-to-many relationships can be mapped using annotations by using the @ManyToMany annotation on the reference variable in the entity class.
    @ManyToMany
    @JoinTable(
       name = "student_course",
       joinColumns = @JoinColumn(name = "course_id"),
       inverseJoinColumns = @JoinColumn(name = "student_id"))
    private Set<Student> students;
    
  3. Join table and join columns in Hibernate many-to-many mapping:

    • The @JoinTable annotation is used to specify the details of the join table, including the join columns.
    @ManyToMany
    @JoinTable(
       name = "student_course",
       joinColumns = @JoinColumn(name = "course_id"),
       inverseJoinColumns = @JoinColumn(name = "student_id"))
    private Set<Student> students;
    
  4. Lazy loading with many-to-many associations in Hibernate:

    • Lazy loading is a technique where associated entities are loaded only when accessed.
    • Use fetch = FetchType.LAZY to enable lazy loading.
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
       name = "student_course",
       joinColumns = @JoinColumn(name = "course_id"),
       inverseJoinColumns = @JoinColumn(name = "student_id"))
    private Set<Student> students;
    
  5. Bidirectional many-to-many mapping in Hibernate:

    • Bidirectional many-to-many mapping involves having a reference in both entities.
    • Use mappedBy attribute in the @ManyToMany annotation to establish bidirectional mapping.
    // In Course class
    @ManyToMany(mappedBy = "courses")
    private Set<Student> students;
    
    // In Student class
    @ManyToMany
    @JoinTable(
       name = "student_course",
       joinColumns = @JoinColumn(name = "student_id"),
       inverseJoinColumns = @JoinColumn(name = "course_id"))
    private Set<Course> courses;
    
  6. Cascading operations in Hibernate many-to-many mapping:

    • Cascading allows propagating operations (save, update, delete) from one entity to its associated entities.
    • Use cascade attribute in the @ManyToMany annotation.
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
       name = "student_course",
       joinColumns = @JoinColumn(name = "course_id"),
       inverseJoinColumns = @JoinColumn(name = "student_id"))
    private Set<Student> students;
    
  7. Extra columns in the join table with Hibernate many-to-many:

    • You can include extra columns in the join table by creating a separate entity for the join table and mapping it accordingly.
    // JoinTable entity
    @Entity
    @Table(name = "student_course")
    public class StudentCourse {
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
    
       @ManyToOne
       @JoinColumn(name = "course_id")
       private Course course;
    
       @ManyToOne
       @JoinColumn(name = "student_id")
       private Student student;
    
       // Additional columns
       // ...
    }
    
  8. HQL queries for many-to-many associations in Hibernate:

    • Use HQL (Hibernate Query Language) to perform queries involving many-to-many associations.
    String hql = "FROM Course c JOIN c.students s WHERE s.name = :studentName";
    Query query = session.createQuery(hql);
    query.setParameter("studentName", "John Doe");
    List<Course> courses = query.list();