밤빵's 개발일지
[TIL]20240817 다대다(Many-to-Many) 연관관계 본문
다대다(Many-to-Many) 연관관계 매핑은 엔티티 간의 복잡한 관계를 설정할 때 자주 사용되는 기법이다. 지난 개발일지에서 연관관계를 기록한적있는데 그떈 내가 프로젝트에서 사용하는 어떤 두 관계에대해서만 중점적으로 다뤘기때문에 이번엔 Spring Boot와 Hibernate를 사용하여 다대다 연관관계를 매핑하는 방법에 대해 공부하고, 예제 코드를 통해 다대다 관계를 어떻게 구현할 수 있는지 예시와 함께 준비했다. 다대다 관계는 하나의 엔티티가 여러 개의 다른 엔티티와 연관될 수 있을 때 사용되며, 실무에서도 자주 사용되는 중요한 개념이다.
🤓다대다(Many-to-Many) 연관관계란?
다대다(Many-to-Many) 연관관계는 두 엔티티 간의 관계에서 하나의 엔티티가 여러 개의 다른 엔티티와 연관되고, 반대로도 여러 개의 다른 엔티티가 하나의 엔티티와 연관될 때 사용된다. 예를 들어, 학생(Student)과 강의(Course)의 관계를 생각해 볼 수 있다. 하나의 학생이 여러 강의를 수강할 수 있고, 하나의 강의에 여러 학생이 등록될 수 있다.
이런 경우, 다대다 관계를 설정하면 된다. 다대다 관계는 중간 테이블을 통해 해결되는데, 이 중간 테이블은 두 개의 외래 키를 가지고 있으며 각각의 외래 키는 두 엔티티의 기본 키를 참조한다.
▶ Spring Boot와 Hibernate를 사용한 다대다 연관관계 매핑
Spring Boot와 Hibernate에서는 @ManyToMany 어노테이션을 사용하여 다대다 관계를 매핑할 수 있다. 하지만, 일반적인 다대다 관계는 중간 테이블을 사용하여 처리하는 것이 좋다. 이렇게 하면 중간 테이블을 명시적으로 정의하고, 추가적인 속성을 저장하거나 관계를 보다 세밀하게 관리할 수 있다.
▶ 코드 예시
이번 예제에서는 Student와 Course 엔티티 간의 다대다 관계를 설정해 보겠다. 각 학생은 여러 강의를 수강할 수 있고, 각 강의는 여러 학생이 등록될 수 있다. 이를 위해 중간 테이블인 Enrollment를 사용하여 학생과 강의 간의 연관관계를 정의한다.
▷엔티티 클래스 정의
▽ Student, Course, Enrollment 엔티티를 간단하게 구현.
// Student.java
package com.example.demo.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
@JoinTable(
name = "enrollment",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
// 생성자, getter 생략
}
// Course.java
package com.example.demo.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<>();
// 생성자, getter 생략
}
// Enrollment.java
package com.example.demo.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "enrollment")
public class Enrollment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "student_id")
private Student student;
@ManyToOne
@JoinColumn(name = "course_id")
private Course course;
private String grade; // 추가적인 속성 예시
// 생성자, getter 생략
}
▶간단한 설명
위 코드에서 Student와 Course는 다대다 관계를 설정하고 있다. @ManyToMany 어노테이션을 사용하여 관계를 정의하고, @JoinTable을 사용하여 중간 테이블(enrollment)을 명시적으로 설정한다. mappedBy 속성은 반대편에서 정의된 필드를 참조하도록 설정하는 데 사용된다. Enrollment 엔티티는 다대다 관계의 중간 테이블을 명시적으로 표현한 것이다. 이 중간 테이블은 두 개의 외래 키(student_id와 course_id)를 가지고 있으며, 두 엔티티 간의 관계를 관리한다. 또한, Enrollment 엔티티에 추가적인 속성(grade)을 정의할 수도 있다.
▶ 서비스와 컨트롤러 설정
▽ 간단한 서비스와 컨트롤러를 만들어, 학생과 강의 간의 관계를 저장하고 조회하는 방법을 예시로 구현했다.
// StudentService.java
package com.example.demo.service;
import com.example.demo.entity.Student;
import com.example.demo.repository.StudentRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentService {
private final StudentRepository studentRepository;
public StudentService(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
public List<Student> getAllStudents() {
return studentRepository.findAll();
}
public Student saveStudent(Student student) {
return studentRepository.save(student);
}
}
// StudentController.java
package com.example.demo.controller;
import com.example.demo.entity.Student;
import com.example.demo.service.StudentService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/students")
public class StudentController {
private final StudentService studentService;
public StudentController(StudentService studentService) {
this.studentService = studentService;
}
@GetMapping
public List<Student> getAllStudents() {
return studentService.getAllStudents();
}
@PostMapping
public Student saveStudent(@RequestBody Student student) {
return studentService.saveStudent(student);
}
}
▶ 다대다 관계의 장단점
→ 장점:
데이터 중복을 줄일 수 있다.
관계를 명확하게 정의할 수 있다.
중간 테이블을 활용하여 추가적인 데이터를 저장하고 관리할 수 있다.
→ 단점:
복잡한 쿼리가 필요할 수 있다.
성능 최적화가 어려울 수 있다.
설계가 복잡해질 수 있다.
▶정리
다대다 관계를 명시적으로 설정하는 것이 다소 복잡할 수 있지만, 중간 테이블을 사용하여 관계를 보다 명확하고 세밀하게 관리할 수 있다는 장점이 있다. 코드 구현을 하면서도 다대다 관계를 이해하는 데 어려워서 이게 맞나 싶었지만 코드를 작성하면서 개념을 알거같기도하고..? 실무에서 많이 쓴다고 하니까 사용할때마다 계속 공부해야하는 부분들인것같다.
'개발Article' 카테고리의 다른 글
[TIL]20240819 기본 포트는 왜 8080일까? (0) | 2024.08.19 |
---|---|
[WIL]20240818 팀 프로젝트 마무리! Redis와 캐싱&세션 (0) | 2024.08.18 |
[TIL]20240816 제이미터와 N+1문제 (0) | 2024.08.16 |
[TIL]20240815 스프링 스케줄러 (0) | 2024.08.15 |
[TIL]20240814 N+1 문제해결 (0) | 2024.08.14 |