How to Implement many-to-many mapping in Spring Data JPA

In this article, we will learn How to implement many-to-many mapping in Spring Data JPA. In case you want to know what is Spring Data JPA check this article https://codedec.com/tutorials/spring-data-jpa-in-spring-boot-with-example/

What is many-to-many Mapping in Spring?

The many-to-many mapping is done between two entities where both can relate to each other. For example, consider a relationship between student and skills; both skills and student relate to each other in a many-to-many way. In Spring Data JPA, it is shown using @ManyToMany mapping.

Following is the  database representation of @ManyToMany Relationship

Here, three tables are created, one is for students, one for skills, and the last one is for holding relationship keys. student_skill is the table created to join students and skills.

Let us implement many-to-many mapping with Spring Data JPA in the Spring boot Application.

Implement many-to-many mapping in Spring Data JPA

Step 1: Open IDE STS- Spring Tool Suite

Step 2: Go to File > Spring Starter Project.

Step 3: Now, fill all the fields as shown below and click Next.

Step 4: Now, Add the dependencies of spring data JPA, MYSQL Driver, Lombok, and spring web.

Now, wait for some time and your project structure will be ready. Go to the pom.xml file and you will see the following dependencies will be added automatically.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
</dependency>

Create a Database in MYSQL

create database many-to-manyjpa;

Configure application. properties file

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/many-to-manyJPA
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name =com.mysql.jdbc.Driver
spring.jpa.show-sql= true
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
#To format SQL queries generated by Hibernate
spring.jpa.properties.hibernate.format_sql=true
#change port number
server.port=8888
  • spring.jpa.hibernate.ddl-auto is set to update so that whatever changes we will do will be reflected in the schema.
  • spring.datasource.url is used to set the URL of the MYSQL DB.
  • spring.datasource.username is used to set the username and spring. datasource. password is used to set the password.
  • spring.datasource.driver-class-name is used to set the driver class name.
  • spring.jpa.show-sql is set to true to show SQL generated by the Hibernate.
  • spring.jpa.properties.hibernate.dialect is used to generate better SQL for the chosen database.
  • spring.jpa.properties.hibernate.format_sql is set to true to format SQL queries.
  • server.port is set to 8888.

Create an Entity Class

Create Student and Skill Entity with @ManyToMany mapping.

Student.java

package com.abc.example.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@RequiredArgsConstructor
@Entity
@Table(name = "student")
public class Student {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private long id;
 @Column(name = "student_name")
 private String studentName;
 @Column(name = "roll_no")
 private long rollNo;
 @Column(name = "qualification")
 private String qualification;
 @ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
 @JoinTable(name = "student_skill",
 joinColumns = {@JoinColumn(name="student_id")},
 inverseJoinColumns = {@JoinColumn(name="skill_id")}
   )
 private Set<Skills> skills = new HashSet<Skills>();
 @Override
 public String toString() {
  return "Student [id=" + id + ", studentName=" + studentName + ", rollNo=" + rollNo + ", qualification="
    + qualification + "]";
 }
 
}

Skills.java

package com.abc.example.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
@RequiredArgsConstructor
@Entity
@Table(name = "skill")
public class Skills {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private long id;
 @Column(name = "skill_name")
 private String skillName;
 @Column(name = "skill_score")
 private String skillsScore;
 @ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL,mappedBy = "skills")
  private Set<Student> students = new HashSet<>();
 @Override
 public String toString() {
  return "Skills [id=" + id + ", skillName=" + skillName + ", skillsScore=" + skillsScore + "]";
 }
 
}
  • @Entity is used to annotate the classes to indicate that they are JPA entities.
  • @Table annotation is used to specify the name of the table that should be mapped with entities.
  • @Id annotation is used for the primary key.
  • The relationship between entities is shown using @ManyToMany mapping. One entity contains a set of other entities and vice versa.
  • I have used the Lombok library to remove boilerplate code. In case you want to know what is Lombok check this article https://codedec.com/tutorials/how-to-configure-lombok-into-eclipse/
  • Cascade.ALL will make sure that all persistence events that are performed on the parent object will be transferred to the child entity.

Following is the mapping part which is the most important part in both classes.

The @JoinTable defines the join table between two entities. (Here, student_skill is the join table). It has @JoinColumn that specifies the name of the column.

Create Repository Interface

The repository here is the DAO layer, which performs all the database operations. StudentRepository and SkillRepository interface is created which will extend JPARepository<ClassName, ID>.

StudentRepository

package com.abc.example.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.abc.example.model.Student;
@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {

}

SkillRepository

package com.abc.example.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.abc.example.model.Skills;
@Repository
public interface SkillRepository extends JpaRepository<Skills, Long> {

 Skills findBySkillName(String string);

}

Now, Go to ManyToManyJpaApplication class and test our many-to-many relationship.

package com.abc.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

import com.abc.example.model.Skills;
import com.abc.example.model.Student;
import com.abc.example.repository.SkillRepository;
import com.abc.example.repository.StudentRepository;

@SpringBootApplication
public class ManyToManyjpaApplication {

 public static void main(String[] args) {
  ApplicationContext context = SpringApplication.run(ManyToManyjpaApplication.class, args);
  StudentRepository studentRepository = context.getBean(StudentRepository.class);
  SkillRepository skillRepository = context.getBean(SkillRepository.class);
  // create student object

  Student student = new Student();
  student.setStudentName("Robert");
  student.setQualification("BE");
  student.setRollNo(5);

  // Add One skills
  Skills skills = new Skills();
  skills.setSkillName("Java");
  skills.setSkillsScore("90%"); 
  // Add another skills
  Skills skills2 = new 		Skills();
  skills2.setSkillName("Python");
  skills2.setSkillsScore("45%");

  // Add skills to Student
  student.getSkills().add(skills);
  student.getSkills().add(skills2);

  // add student to skills;
  skills.getStudents().add(student);
  skills2.getStudents().add(student);

  // saving..
  studentRepository.save(student); //we have use CASCADE_ALL 
   
 }

}

Now, Go to Run As > Spring Boot App. The tables will be created inside the database along with mapping.

Now, if we want to fetch records add the following code in the Application class:

Skills skills4= skillRepository.findBySkillName("Java"); 
System.out.println("List" + skills4); 
System.out.println(studentRepository.findAll());

Run the Application once again and see the console for output. In this way, we learned how to map many-to-many relationships in Spring Data JPA.