Hibernate Many-to-Many Mapping Example

In this tutorial, we will demonstrate how to set up a many-to-many mapping between two entities using Hibernate annotations. We will create an example with Student and Course entities to illustrate this mapping and cover CRUD operations.

Prerequisites

  1. Java Development Kit (JDK) 21 or higher: Ensure JDK is installed and configured on your system.
  2. Integrated Development Environment (IDE): IntelliJ IDEA, Eclipse, or any other IDE.
  3. Maven: Ensure Maven is installed and configured on your system.

Step 1: Create a Maven Project

  1. Open your IDE and create a new Maven project.
  2. Update the pom.xml file to include Hibernate and other required dependencies.
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>hibernate-many-to-many-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.2.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>
    </dependencies>
</project>

Explanation

  • Hibernate Core Dependency: Includes the main Hibernate framework.
  • SLF4J Dependencies: Used for logging.
  • H2 Database Dependency: An in-memory database for testing purposes.

Step 2: Create Hibernate Configuration File

Create a file named hibernate.cfg.xml in the src/main/resources directory.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
        <property name="hibernate.connection.driver_class">org.h2.Driver</property>
        <property name="hibernate.connection.url">jdbc:h2:mem:testdb</property>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.connection.password"></property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
    </session-factory>
</hibernate-configuration>

Explanation

  • Dialect: Specifies the SQL dialect (H2 in this case).
  • Connection Properties: Configure the JDBC connection to the H2 database.
  • hbm2ddl.auto: Automatically manages the database schema (update existing schema).
  • show_sql: Prints SQL statements to the console.
  • format_sql: Formats SQL statements.

Step 3: Create the Student Entity Class

Create a package named com.example.entity and a class named Student.

package com.example.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.ALL)
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses = new HashSet<>();

    public Student() {}

    public Student(String name) {
        this.name = name;
    }

    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 setCourses(Set<Course> courses) {
        this.courses = courses;
    }

    public void addCourse(Course course) {
        courses.add(course);
        course.getStudents().add(this);
    }

    public void removeCourse(Course course) {
        courses.remove(course);
        course.getStudents().remove(this);
    }

    @Override
    public String toString() {
        return "Student{id=" + id + ", name='" + name + '\'' + '}';
    }
}

Explanation

  • @Entity: Marks the class as an entity.
  • @Id: Marks the field as the primary key.
  • @GeneratedValue: Specifies the strategy for generating values for the primary key.
  • @ManyToMany: Defines a many-to-many relationship with the Course entity.
  • @JoinTable: Specifies the join table.
  • @JoinColumn: Specifies the foreign key column for the current entity.
  • @inverseJoinColumns: Specifies the foreign key column for the other entity in the relationship.

Step 4: Create the Course Entity Class

Create a class named Course in the same package.

package com.example.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 name;

    @ManyToMany(mappedBy = "courses")
    private Set<Student> students = new HashSet<>();

    public Course() {}

    public Course(String name) {
        this.name = name;
    }

    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<Student> getStudents() {
        return students;
    }

    public void setStudents(Set<Student> students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "Course{id=" + id + ", name='" + name + '\'' + '}';
    }
}

Explanation

  • @Entity: Marks the class as an entity.
  • @Id: Marks the field as the primary key.
  • @GeneratedValue: Specifies the strategy for generating values for the primary key.
  • @ManyToMany: Defines a many-to-many relationship with the Student entity.
  • mappedBy: Specifies the field in the Student entity that owns the relationship.

Step 5: Create a Hibernate Utility Class

Create a package named com.example.util and a class named HibernateUtil.

package com.example.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil {
    private static SessionFactory sessionFactory;

    static {
        try {
            Configuration configuration = new Configuration().configure();
            ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
                    .applySettings(configuration.getProperties()).build();
            sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

Explanation

  • Configuration: Loads Hibernate configuration from hibernate.cfg.xml.
  • ServiceRegistry: Builds the service registry from the configuration settings.
  • SessionFactory: Provides sessions to interact with the database.

Step 6: Create Main Class

Create a package named com.example and a class named Main.

package com.example;

import com.example.entity.Course;
import com.example.entity.Student;
import com.example.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class Main {
    public static void main(String[] args) {
        // Initialize session and transaction
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();

        // Create students
        Student student1 = new Student("John Doe");
        Student student2 = new Student("Jane Doe");

        // Create courses
        Course course1 = new Course("Math");
        Course course2 = new Course("Science");

        // Add courses to students
        student1.addCourse(course1);
        student1.addCourse(course2);
        student2.addCourse(course1);

        // Save students (this will also

 save courses due to cascade)
        session.save(student1);
        session.save(student2);
        transaction.commit();
        session.close();

        // Retrieve and display students
        session = HibernateUtil.getSessionFactory().openSession();
        Student retrievedStudent1 = session.get(Student.class, student1.getId());
        Student retrievedStudent2 = session.get(Student.class, student2.getId());
        System.out.println("Retrieved Student 1: " + retrievedStudent1);
        System.out.println("Courses: " + retrievedStudent1.getCourses());
        System.out.println("Retrieved Student 2: " + retrievedStudent2);
        System.out.println("Courses: " + retrievedStudent2.getCourses());
        session.close();

        // Update a student's course
        session = HibernateUtil.getSessionFactory().openSession();
        transaction = session.beginTransaction();
        Student studentToUpdate = session.get(Student.class, student1.getId());
        studentToUpdate.removeCourse(course2);
        session.update(studentToUpdate);
        transaction.commit();
        session.close();

        // Delete a student
        session = HibernateUtil.getSessionFactory().openSession();
        transaction = session.beginTransaction();
        Student studentToDelete = session.get(Student.class, student2.getId());
        session.delete(studentToDelete);
        transaction.commit();
        session.close();

        // Retrieve and display updated students
        session = HibernateUtil.getSessionFactory().openSession();
        Student updatedStudent1 = session.get(Student.class, student1.getId());
        System.out.println("Updated Student 1: " + updatedStudent1);
        System.out.println("Courses: " + updatedStudent1.getCourses());
        session.close();

        // Close the SessionFactory
        HibernateUtil.getSessionFactory().close();
    }
}

Explanation

  • Session: Opens a session to interact with the database.
  • Transaction: Begins and commits a transaction for database operations.
  • Save: Persists the entity to the database.
  • Retrieve: Fetches the entity from the database using its ID.
  • Update: Updates the entity in the database.
  • Delete: Removes the entity from the database.

Step 7: Run the Application

  1. Run the Main class.
  2. The output in the console should be:
Hibernate: create table Course (id bigint generated by default as identity, name varchar(255), primary key (id))
Hibernate: create table Student (id bigint generated by default as identity, name varchar(255), primary key (id))
Hibernate: create table student_course (student_id bigint not null, course_id bigint not null, primary key (student_id, course_id))
Hibernate: alter table student_course add constraint FKk8ue48wh82jw46hmhp8u6up1o foreign key (course_id) references Course
Hibernate: alter table student_course add constraint FKp0fhshy3x4bdojl88kxwsh7hf foreign key (student_id) references Student
Hibernate: insert into Course (name) values (?)
Hibernate: insert into Course (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into student_course (student_id, course_id) values (?, ?)
Hibernate: insert into student_course (student_id, course_id) values (?, ?)
Hibernate: insert into student_course (student_id, course_id) values (?, ?)
Hibernate: select student0_.id as id1_1_0_, student0_.name as name2_1_0_ from Student student0_ where student0_.id=?
Hibernate: select courses0_.student_id as student_1_3_0_, courses0_.course_id as course_i2_3_0_, course1_.id as id1_0_1_, course1_.name as name2_0_1_ from student_course courses0_ inner join Course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Retrieved Student 1: Student{id=1, name='John Doe'}
Courses: [Course{id=1, name='Math'}, Course{id=2, name='Science'}]
Hibernate: select student0_.id as id1_1_0_, student0_.name as name2_1_0_ from Student student0_ where student0_.id=?
Hibernate: select courses0_.student_id as student_1_3_0_, courses0_.course_id as course_i2_3_0_, course1_.id as id1_0_1_, course1_.name as name2_0_1_ from student_course courses0_ inner join Course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Retrieved Student 2: Student{id=2, name='Jane Doe'}
Courses: [Course{id=1, name='Math'}]
Hibernate: select student0_.id as id1_1_0_, student0_.name as name2_1_0_ from Student student0_ where student0_.id=?
Hibernate: select courses0_.student_id as student_1_3_0_, courses0_.course_id as course_i2_3_0_, course1_.id as id1_0_1_, course1_.name as name2_0_1_ from student_course courses0_ inner join Course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Hibernate: delete from student_course where student_id=? and course_id=?
Hibernate: update Student set name=? where id=?
Hibernate: select student0_.id as id1_1_0_, student0_.name as name2_1_0_ from Student student0_ where student0_.id=?
Hibernate: select courses0_.student_id as student_1_3_0_, courses0_.course_id as course_i2_3_0_, course1_.id as id1_0_1_, course1_.name as name2_0_1_ from student_course courses0_ inner join Course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Hibernate: select student0_.id as id1_1_0_, student0_.name as name2_1_0_ from Student student0_ where student0_.id=?
Hibernate: select courses0_.student_id as student_1_3_0_, courses0_.course_id as course_i2_3_0_, course1_.id as id1_0_1_, course1_.name as name2_0_1_ from student_course courses0_ inner join Course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Hibernate: delete from student_course where student_id=?
Hibernate: delete from Student where id=?
Hibernate: select student0_.id as id1_1_0_, student0_.name as name2_1_0_ from Student student0_ where student0_.id=?
Hibernate: select courses0_.student_id as student_1_3_0_, courses0_.course_id as course_i2_3_0_, course1_.id as id1_0_1_, course1_.name as name2_0_1_ from student_course courses0_ inner join Course course1_ on courses0_.course_id=course1_.id where courses0_.student_id=?
Updated Student 1: Student{id=1, name='John Doe'}
Courses: [Course{id=1, name='Math'}]

Conclusion

You have successfully created an example using Hibernate to demonstrate a many-to-many mapping with annotations. This tutorial covered setting up a Maven project, configuring Hibernate, creating entity classes with a many-to-many relationship, and performing CRUD operations.


Comments