Mapping - @ManyToMany

In a @ManyToMany relationship, multiple instances of one entity are associated with multiple instances of another entity. This tutorial demonstrates how to implement a @ManyToMany mapping using JPA and EntityManager.


Example Scenario

Let’s consider the following entities:

  1. Student: Represents a student in a school.
  2. Course: Represents a course offered by the school.

A student can enroll in multiple courses, and a course can have multiple students enrolled.


Step-by-Step Implementation

Step 1: Create Entity Classes

  1. Student Entity
import jakarta.persistence.*; import java.util.HashSet; import java.util.Set; @Entity public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable( name = "student_course", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "course_id") ) private Set<Course> courses = new HashSet<>(); // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Course> getCourses() { return courses; } public void addCourse(Course course) { courses.add(course); course.getStudents().add(this); // Establish bi-directional relationship } public void removeCourse(Course course) { courses.remove(course); course.getStudents().remove(this); // Break bi-directional relationship } }
  1. Course Entity
import jakarta.persistence.*; import java.util.HashSet; import java.util.Set; @Entity public class Course { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; @ManyToMany(mappedBy = "courses", cascade = {CascadeType.PERSIST, CascadeType.MERGE}) private Set<Student> students = new HashSet<>(); // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Set<Student> getStudents() { return students; } }

Step 2: Persistence Configuration

Configure your persistence.xml:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1"> <persistence-unit name="example-unit"> <class>Student</class> <class>Course</class> <properties> <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:testdb"/> <property name="jakarta.persistence.jdbc.user" value="sa"/> <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/> <property name="jakarta.persistence.jdbc.password" value=""/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> </properties> </persistence-unit> </persistence>

Step 3: Using EntityManager

Create a main class to demonstrate the relationship.

import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; public class ManyToManyExample { public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit"); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); // Create Students Student student1 = new Student(); student1.setName("Alice"); Student student2 = new Student(); student2.setName("Bob"); // Create Courses Course course1 = new Course(); course1.setTitle("Mathematics"); Course course2 = new Course(); course2.setTitle("Physics"); // Establish relationships student1.addCourse(course1); student1.addCourse(course2); student2.addCourse(course1); // Persist students (courses will be persisted automatically due to CascadeType.PERSIST) em.persist(student1); em.persist(student2); em.getTransaction().commit(); // Fetch and display Student fetchedStudent = em.find(Student.class, student1.getId()); System.out.println("Student: " + fetchedStudent.getName()); System.out.println("Enrolled Courses:"); fetchedStudent.getCourses().forEach(course -> System.out.println(" - " + course.getTitle())); em.close(); emf.close(); } }

Key Points:

  1. Mapping Annotations:

    • @ManyToMany: Declares the relationship in both entities.
    • @JoinTable: Specifies the join table and its join columns.
    • mappedBy: Used in the non-owning side of the relationship.
  2. Cascade Operations:

    • CascadeType.PERSIST and CascadeType.MERGE: Ensure that related entities are persisted or merged automatically.
  3. Bidirectional Relationship:

    • Both Student and Course maintain references to each other to reflect the relationship.
  4. Join Table:

    • The join table (student_course) will have two columns: student_id and course_id.

Comments