Last updated on March 14th, 2024
This topic teaches us how to implement many-to-many unidirectional mapping between two JPA entities using Spring Boot, Spring Data JPA, H2 database and Lombok. We will take Student and Subject entities to implement many-to-many unidirectional mapping to generate a third table which contains foreign keys of tables. This mapping navigates in one direction(Student to Subject). The diagram representation of this mapping is shown below.
We will create a restful web service example to perform many-to-many unidirectional mapping using the Spring Boot application step-by-step. Let’s begin to implement
1. Creating a Spring Boot Starter Project
We are creating a Spring Boot Application from the web tool Spring Initializr.
Add the following dependencies:
• Spring Web
• Lombok
• H2 Database
• Spring Data JPA
Project Structure of Many-to-Many Unidirectional Mapping
2. Maven Dependency
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.springjava</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. Define the H2 database configuration
We are configuring the H2 database configuration in the application.properties file
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
4. Creating JPA Entities
We are creating two JPA entity classes Student and Subject.
Student.java
package com.springjava.entity;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
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 lombok.Data;
@Data
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String email;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "student_subject",
joinColumns = { @JoinColumn(name = "stu_id") },
inverseJoinColumns = { @JoinColumn(name = "sub_id") })
private List<Subject> subList;
}
→ We used @ManyToMany annotation in this class to make many-to-many mapping with the Subject entity class.
→ We used @JoinTable annotation to generate a third table with the mentioned name which contains foreign keys with mentioned names.
Subject.java
package com.springjava.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Data;
@Data
@Entity
public class Subject {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
→ This @Data annotation is used to generate this class’s constructor, setter method, getter method, etc.
5. Creating Spring Data JPA Repositories
StudentRepository.java
package com.springjava.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.springjava.entity.Student;
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
SubjectRepository.java
package com.springjava.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.springjava.entity.Subject;
public interface SubjectRepository extends JpaRepository<Subject, Integer> {
}
6. Creating Service Interfaces and Classes
We are creating one interface StudentService and a class implementation StudentServiceImpl of that service interface.
StudentService.java
package com.springjava.service;
import java.util.List;
import com.springjava.entity.Student;
public interface StudentService {
void save(Student student);
List<Student> findAll();
}
StudentServiceImpl.java
package com.springjava.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springjava.entity.Student;
import com.springjava.repository.StudentRepository;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentRepository stuRepo;
@Override
public void save(Student student) {
stuRepo.save(student);
}
@Override
public List<Student> findAll() {
return stuRepo.findAll();
}
}
We are creating one interface SubjectService and a class implementation SubjectServiceImpl of that service interface.
SubjectService.java
package com.springjava.service;
import java.util.List;
import com.springjava.entity.Subject;
public interface SubjectService {
List<Subject> findAll();
}
SubjectServiceImpl.java
package com.springjava.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springjava.entity.Subject;
import com.springjava.repository.SubjectRepository;
@Service
public class SubjectServiceImpl implements SubjectService {
@Autowired
SubjectRepository subRepo;
@Override
public List<Subject> findAll() {
return subRepo.findAll();
}
}
7. Creating Data Model class
We are creating a data model class which is a Java Bean class for passing request body parameters in the controller class.
StudentModel.java
package com.springjava.model;
import java.util.List;
import com.springjava.entity.Subject;
import lombok.Data;
@Data
public class StudentModel {
private String name;
private String email;
private List<Subject>subjects;
}
8. Creating Controller classes
We are creating two controller classes StudentController and SubjectController.
StudentController.java
package com.springjava.controller;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.springjava.entity.Student;
import com.springjava.model.StudentModel;
import com.springjava.service.StudentService;
@RestController
@RequestMapping("/api")
public class StudentController {
@Autowired
StudentService stuService;
@PostMapping("/student/save")
public ResponseEntity<?> save(@RequestBody StudentModel student) {
Map<String, Object> respStu = new LinkedHashMap<String, Object>();
Student stu = new Student();
stu.setName(student.getName());
stu.setEmail(student.getEmail());
stu.setSubList(student.getSubjects());
stuService.save(stu);
respStu.put("status", 1);
respStu.put("message", "Record is Saved Successfully!");
return new ResponseEntity<>(respStu, HttpStatus.CREATED);
}
@GetMapping("/student/list")
public ResponseEntity<?> getStudents() {
Map<String, Object> respStu = new LinkedHashMap<String, Object>();
List<Student> studList = stuService.findAll();
if (!studList.isEmpty()) {
respStu.put("status", 1);
respStu.put("data", studList);
return new ResponseEntity<>(respStu, HttpStatus.OK);
} else {
respStu.clear();
respStu.put("status", 0);
respStu.put("message", "Data is not found");
return new ResponseEntity<>(respStu, HttpStatus.NOT_FOUND);
}
}
}
SubjectController.java
package com.springjava.controller;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.springjava.entity.Subject;
import com.springjava.service.SubjectService;
@RestController
@RequestMapping("/api")
public class SubjectController {
@Autowired
SubjectService subService;
@GetMapping("/subject/list")
public ResponseEntity<?> getSubjects() {
Map<String, Object> respSub = new LinkedHashMap<String, Object>();
List<Subject> subList=subService.findAll();
if (!subList.isEmpty()) {
respSub.put("status", 1);
respSub.put("data", subList);
return new ResponseEntity<>(respSub, HttpStatus.OK);
} else {
respSub.clear();
respSub.put("status", 0);
respSub.put("message", "Data is not found");
return new ResponseEntity<>(respSub, HttpStatus.NOT_FOUND);
}
}
}
9. Run the Spring Boot Application
To run this application many-to-many unidirectional mapping Right-click on the DemoApplication.java then click on Run As and select Java Application.
To test the API on the Postman
Url: http://localhost:8080/api/student/save
Url: http://localhost:8080/api/student/list
Url: http://localhost:8080/api/subject/list
To check the H2 database we can browse this URL “http://localhost:8080/h2-console” on the browser to view the tables in the database which are created by this spring boot application(many-to-many unidirectional mapping).
See both tables below here:
9. Conclusion
In this topic, we learnt how to implement many-to-many unidirectional mapping in spring boot, spring data JPA, Lombok and H2 database with rest API example.