Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20240817 다대다(Many-to-Many) 연관관계 본문

개발Article

[TIL]20240817 다대다(Many-to-Many) 연관관계

최밤빵 2024. 8. 17. 19:25

다대다(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);
    }
}

 

▶ 다대다 관계의 장단점

 

→ 장점:

데이터 중복을 줄일 수 있다.

관계를 명확하게 정의할 수 있다.

중간 테이블을 활용하여 추가적인 데이터를 저장하고 관리할 수 있다.

→ 단점:

복잡한 쿼리가 필요할 수 있다.

성능 최적화가 어려울 수 있다.

설계가 복잡해질 수 있다.

 

▶정리

다대다 관계를 명시적으로 설정하는 것이 다소 복잡할 수 있지만, 중간 테이블을 사용하여 관계를 보다 명확하고 세밀하게 관리할 수 있다는 장점이 있다. 코드 구현을 하면서도 다대다 관계를 이해하는 데 어려워서 이게 맞나 싶었지만 코드를 작성하면서 개념을 알거같기도하고..? 실무에서 많이 쓴다고 하니까 사용할때마다 계속 공부해야하는 부분들인것같다.