Spring Boot Many-to-One Mapping
~1 min read
·
Spring Boot
Many-to-Many relationships occur when multiple entities relate to multiple entities. Example:
Students can enroll in multiple Courses Courses can have multiple Students
Define Student and Course Entities We'll create a bidirectional Many-to-Many relationship between Student and Course
Student.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
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")
)
@JsonIgnoreProperties("students") // ✅ Prevents infinite loop
private Set 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 getCourses() {
return courses;
}
public void setCourses(Set courses) {
this.courses = courses;
}
}
show = true, 3000)" class="absolute top-[10px] right-[10px] inline-flex items-center justify-center gap-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-smooth h-9 rounded-md px-3 cursor-pointer bg-white/80 border border-border">
✅ This creates a join table student_course to link students and courses.
Course.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
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") // No @JoinTable here (already in Student)
@JsonIgnoreProperties("courses") // ✅ Prevents infinite loop
private Set 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 getStudents() {
return students;
}
public void setStudents(Set students) {
this.students = students;
}
}
show = true, 3000)" class="absolute top-[10px] right-[10px] inline-flex items-center justify-center gap-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-smooth h-9 rounded-md px-3 cursor-pointer bg-white/80 border border-border">
✅ Bidirectional Mapping: The relationship is mapped from Student to Course .
Table name : student_course
Create JPA Repositories
StudentRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
public interface StudentRepository extends JpaRepository {
}
show = true, 3000)" class="absolute top-[10px] right-[10px] inline-flex items-center justify-center gap-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-smooth h-9 rounded-md px-3 cursor-pointer bg-white/80 border border-border">
CourseRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
public interface CourseRepository extends JpaRepository {
}
show = true, 3000)" class="absolute top-[10px] right-[10px] inline-flex items-center justify-center gap-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-smooth h-9 rounded-md px-3 cursor-pointer bg-white/80 border border-border">
Create Services
StudentService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class StudentService {
@Autowired
private StudentRepository studentRepository;
@Autowired
private CourseRepository courseRepository;
// ✅ Create Student & Courses
public Student createStudentWithCourse(Student student) {
Set savedCourses = student.getCourses().stream()
.map(course -> courseRepository.save(course))
.collect(Collectors.toSet());
student.setCourses(savedCourses);
return studentRepository.save(student);
}
// ✅ Create Student & Assign Courses
public Student createStudent(StudentDTO studentDTO) {
Student student = new Student();
student.setName(studentDTO.getName());
Set courses = studentDTO.getCourseIds().stream()
.map(courseRepository::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
student.setCourses(courses);
return studentRepository.save(student);
}
// ✅ Create Student Only
public Student createStudentOnly(Student student) {
return studentRepository.save(student);
}
// ✅ Get Student by ID
public Student getStudent(Long id) {
return studentRepository.findById(id).orElseThrow(() -> new RuntimeException("Student Not Found"));
}
// ✅ Add Courses to an Existing Student
public Student addCoursesToStudent(Long studentId, Set courseIds) {
Student student = studentRepository.findById(studentId).orElseThrow(() -> new RuntimeException("Student Not Found"));
Set courses = courseIds.stream()
.map(courseRepository::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
student.getCourses().addAll(courses);
return studentRepository.save(student);
}
// ✅ Delete Student
public String deleteStudent(Long studentId) {
studentRepository.deleteById(studentId);
return "Deleted Successfully";
}
}
show = true, 3000)" class="absolute top-[10px] right-[10px] inline-flex items-center justify-center gap-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-smooth h-9 rounded-md px-3 cursor-pointer bg-white/80 border border-border">
Create Controllers
StudentController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
@RestController
@RequestMapping("/students")
public class StudentController {
@Autowired
private StudentService studentService;
// ✅ Create Student with Courses
@PostMapping
public ResponseEntity createStudentWithCourse(@RequestBody Student student) {
return ResponseEntity.ok(studentService.createStudentWithCourse(student));
}
// ✅ Create Student with Courses
@PostMapping("/createStudent")
public ResponseEntity createStudent(@RequestBody StudentDTO studentDTO) {
return ResponseEntity.ok(studentService.createStudent(studentDTO));
}
// ✅ Create Student with Courses
@PostMapping("/createStudentOnly")
public ResponseEntity createStudentOnly(@RequestBody Student student) {
return ResponseEntity.ok(studentService.createStudentOnly(student));
}
// ✅ Get Student with Courses
@GetMapping("/{id}")
public ResponseEntity getStudent(@PathVariable Long id) {
return ResponseEntity.ok(studentService.getStudent(id));
}
// ✅ Add Courses to Existing Student
@PutMapping("/{id}/courses")
public ResponseEntity addCourses(@PathVariable Long id, @RequestBody Set courseIds) {
return ResponseEntity.ok(studentService.addCoursesToStudent(id, courseIds));
}
// ✅ Add Courses to Existing Student
@DeleteMapping("/{id}")
public ResponseEntity deleteCourses(@PathVariable Long id) {
return ResponseEntity.ok(studentService.deleteStudent(id));
}
}
show = true, 3000)" class="absolute top-[10px] right-[10px] inline-flex items-center justify-center gap-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-smooth h-9 rounded-md px-3 cursor-pointer bg-white/80 border border-border">
Testing the REST API with Postman: Once the application is running, test the endpoints:
Create Students with Courses POST:http://localhost:8080/students
Create a Student with Existing Courses POST:http://localhost:8080/students/createStudent
Get Student with Courses GET:http://localhost:8080/students/{studentID}
Create Student Only POST:http://localhost:8080/students/createStudentOnly
Add More Courses to Student PUT:http://localhost:8080/students/{studentID}/courses
Delete Courses with Student DELETE:http://localhost:8080/students/{studentID}