1. 개요

이 사용방법에서는 JPA를 사용하여 다 대다 관계처리하는 여러 방법을 살펴 봅니다 .

우리는 학생들, 코스 및 그들 사이의 다양한 관계의 모델을 사용할 것입니다.

단순성을 위해 코드 예제에서는 다 대다 관계와 관련된 속성 및 JPA 구성 만 표시합니다.

2. 기본 다 대다

2.1. 다 대다 관계 모델링

관계는 두 유형의 엔터티 간의 연결입니다. 다 대다 관계의 경우 양쪽 모두 다른 쪽의 여러 인스턴스와 관련 될 수 있습니다.

엔티티 유형이 자신과 관계를 맺을 수 있습니다. 가계도 모델링의 예를 생각해보십시오. 모든 노드는 사람이므로 부모-자녀 관계에 대해 이야기하면 두 참가자 모두 사람이됩니다.

그러나 단일 또는 다중 엔티티 유형 간의 관계에 대해 이야기하는지 여부는 그러한 차이를 만들지 않습니다. 서로 다른 두 엔터티 유형 간의 관계에 대해 생각하는 것이 더 쉽기 때문에이를 사용하여 사례를 설명합니다.

학생들이 좋아하는 코스를 표시하는 예를 들어 보겠습니다.

학생은 여러 코스 를 좋아할 수 있으며 많은 학생이 동일한 코스를 좋아할 수 있습니다.

아시다시피 RDBMS에서는 외래 키와의 관계를 만들 수 있습니다. 양쪽 모두 다른 쪽을 참조 할 수 있어야 하므로 외래 키를 보관할 별도의 테이블을 만들어야합니다 .

이러한 테이블을 조인 테이블 이라고 합니다. 조인 테이블에서 외래 키 조합은 복합 기본 키가됩니다.

2.2. JPA에서 구현

POJO와 다 대다 관계를 모델링하는 것은 쉽습니다. 다른 클래스 의 요소를 포함하는 컬렉션 을 두 클래스 모두에 포함 해야 합니다.

그런 다음 클래스를 @Entity 로 표시  하고 기본 키를 @Id 로 표시하여 적절한 JPA 엔티티를 만들어야합니다.

또한 관계 유형을 구성해야합니다. 따라서 @ManyToMany 어노테이션으로 컬렉션을 표시합니다 .

@Entity
class Student {

    @Id
    Long id;

    @ManyToMany
    Set<Course> likedCourses;

    // additional properties
    // standard constructors, getters, and setters
}

@Entity
class Course {

    @Id
    Long id;

    @ManyToMany
    Set<Student> likes;

    // additional properties
    // standard constructors, getters, and setters
}

또한 RDBMS에서 관계를 모델링하는 방법을 구성해야합니다.

소유자 측은 관계를 구성하는 곳입니다. Student 클래스를 사용할 것 입니다.

Student 클래스 @JoinTable 어노테이션으로 이를 수행 할 수 있습니다 . @JoinColumn 어노테이션 을 사용하여 조인 테이블 ( course_like ) 의 이름과 외래 키를 제공합니다 . joinColumn의 특성은 관계의 소유자 측과 연결한다 inverseJoinColumn 반대편 :

@ManyToMany
@JoinTable(
  name = "course_like", 
  joinColumns = @JoinColumn(name = "student_id"), 
  inverseJoinColumns = @JoinColumn(name = "course_id"))
Set<Course> likedCourses;

사용합니다 @JoinTable  또는 @JoinColumn하는 것은 필요하지 않습니다. JPA는 테이블 및 열 이름을 생성합니다. 그러나 JPA에서 사용하는 전략은 우리가 사용하는 명명 규칙과 항상 일치하지는 않습니다. 따라서 테이블 및 열 이름을 구성 할 수있는 가능성이 필요합니다.

대상 측에서는 관계를 매핑하는 필드의 이름 만 제공하면됩니다.

따라서, 우리는 설정 mappedBy의 의 속성 @ManyToMany의 에서 어노테이션 코스 클래스를 :

@ManyToMany(mappedBy = "likedCourses")
Set<Student> likes;

대다 관계는 데이터베이스에 소유자 측이 없기 때문에 Course 클래스 에 조인 테이블을 구성 하고 Student 클래스 에서 참조 할 수 있습니다 .

3. 복합 키를 사용하는 다 대다

3.1. 관계 속성 모델링

학생들이 코스를 평가하게하고 싶다고 가정 해 보겠습니다. 한 학생은 여러 코스를 평가할 수 있으며 여러 학생이 동일한 코스를 평가할 수 있습니다. 따라서 다 대다 관계이기도합니다.

이 예제를 좀 더 복잡하게 만드는 것은 평가 관계가 존재한다는 사실보다 더 많은 관계가 있다는 것입니다. 학생이 코스에 부여한 평가 점수를 저장해야합니다.

이 정보를 어디에 저장할 수 있습니까? 학생이 다른 코스에 다른 등급을 부여 할 수 있으므로 Student 엔터티 에 넣을 수 없습니다 . 마찬가지로 Course 엔터티 에 저장 하는 것도 좋은 솔루션이 아닙니다.

관계 자체에 속성 이있는 상황 입니다.

이 예제를 사용하여 관계에 속성을 첨부하는 것은 ER 다이어그램에서 다음과 같습니다.

간단한 다 대다 관계와 거의 동일한 방식으로 모델링 할 수 있습니다. 유일한 차이점은 조인 테이블에 새 속성을 추가한다는 것입니다.

3.2. JPA에서 복합 키 만들기

단순한 다 대다 관계의 구현은 다소 간단했습니다. 유일한 문제는 엔터티를 직접 연결했기 때문에 관계에 속성을 추가 할 수 없다는 것입니다. 따라서 관계 자체에 속성을 추가 할 방법이 없었습니다.

DB 속성을 JPA의 클래스 필드에 매핑하므로 관계에 대한 새 엔티티 클래스를 만들어야합니다.

물론 모든 JPA 엔터티에는 기본 키가 필요합니다. 기본 키는 복합 키이므로 키의 다른 부분을 포함 할 새 클래스를 만들어야합니다 .

@Embeddable
class CourseRatingKey implements Serializable {

    @Column(name = "student_id")
    Long studentId;

    @Column(name = "course_id")
    Long courseId;

    // standard constructors, getters, and setters
    // hashcode and equals implementation
}

복합 키 클래스는 몇 가지 주요 요구 사항 을 충족 해야합니다 .

  • @Embeddable 로 표시해야합니다 .
  • java.io.Serializable 을 구현해야 합니다.
  • hashcode ()equals () 메서드 의 구현을 제공해야 합니다.
  • 필드 자체는 엔티티가 될 수 없습니다.

3.3. JPA에서 복합 키 사용

이 복합 키 클래스를 사용하여 조인 테이블을 모델링하는 엔터티 클래스를 만들 수 있습니다.

@Entity
class CourseRating {

    @EmbeddedId
    CourseRatingKey id;

    @ManyToOne
    @MapsId("studentId")
    @JoinColumn(name = "student_id")
    Student student;

    @ManyToOne
    @MapsId("courseId")
    @JoinColumn(name = "course_id")
    Course course;

    int rating;
    
    // standard constructors, getters, and setters
}

이 코드는 일반 엔티티 구현과 매우 유사합니다. 그러나 몇 가지 주요 차이점이 있습니다.

  • @EmbeddedId  를 사용 하여 CourseRatingKey 클래스 의 인스턴스 인 기본 키를 표시했습니다 .
  • 학생코스 필드를 @MapsId 로 표시했습니다 .

@MapsId 는 이러한 필드를 키의 일부에 연결하고 다 대일 관계의 외래 키임을 의미합니다. 앞서 언급했듯이 복합 키에 엔터티를 포함 할 수 없기 때문에 필요합니다.

그런 다음 이전 같이 StudentCourse 엔터티 에서 역 참조를 구성 할 수 있습니다 .

class Student {

    // ...

    @OneToMany(mappedBy = "student")
    Set<CourseRating> ratings;

    // ...
}

class Course {

    // ...

    @OneToMany(mappedBy = "course")
    Set<CourseRating> ratings;

    // ...
}

복합 키를 사용하는 다른 방법 인 @IdClass 어노테이션이 있습니다.

3.4. 추가 특성

StudentCourse 클래스에 대한 관계를 @ManyToOne으로 구성했습니다 . 새로운 엔터티를 사용하여 다 대다 관계를 두 개의 다 대일 관계로 구조적으로 분해했기 때문에 이렇게 할 수 있습니다.

왜 우리가 이것을 할 수 있었습니까? 이전 사례에서 테이블을 자세히 살펴보면 두 개의 다 대일 관계가 포함되어 있음을 알 수 있습니다. 즉, RDBMS에는 다 대다 관계가 없습니다. 우리가 모델링하는 것이기 때문에 조인 테이블 다 대다 관계로 생성 한 구조를 호출합니다.

게다가 다 대다 관계에 대해 이야기하는 것이 우리의 의도이기 때문에 더 분명합니다. 한편 조인 테이블은 구현 세부 사항 일뿐입니다. 우리는 그것에 대해 정말로 신경 쓰지 않습니다.

또한이 솔루션에는 아직 언급하지 않은 추가 기능이 있습니다. 간단한 다 대다 솔루션은 두 엔터티 간의 관계를 만듭니다. 따라서 우리는 더 많은 엔티티로 관계를 확장 할 수 없습니다. 그러나이 솔루션에는 이러한 제한이 없습니다 . 원하는 수의 항목 유형 간의 관계를 모델링 할 수 있습니다.

예를 들어 여러 교사가 한 코스를 가르 칠 수있는 경우 학생은 특정 교사가 특정 코스를 어떻게 가르치는 지 평가할 수 있습니다. 그런 식으로 등급은 학생, 코스 및 교사의 세 항목 간의 관계가됩니다.

4. 새 엔티티를 사용한 다 대다

4.1. 관계 속성 모델링

학생들이 코스에 등록 할 수 있도록하고 싶다고 가정 해 보겠습니다. 또한 학생이 특정 과정에 등록 할 때 포인트를 저장해야합니다. 또한 그녀가받은 성적을 과정에서 저장하고 싶습니다.

이상적인 세계에서는 복합 키가있는 엔티티가있는 이전 솔루션으로이 문제를 해결할 수 있습니다. 그러나 세상은 이상적인 것과는 거리가 멀고 학생들이 항상 첫 번째 시도에서 과정을 이수하는 것은 아닙니다.

이 경우 동일한 student_id-course_id 쌍이 있는 동일한 student-course 쌍 또는 여러 행 사이여러 연결 이 있습니다 . 모든 기본 키가 고유해야하므로 이전 솔루션을 사용하여 모델링 할 수 없습니다. 따라서 별도의 기본 키를 사용해야합니다.

따라서 등록 속성을 보유 할 엔티티를 도입 할 수 있습니다 .

이 경우 등록 엔터티는 다른 두 엔터티 간의 관계나타냅니다 .

엔티티이기 때문에 자체 기본 키가 있습니다.

이전 솔루션에서는 두 개의 외래 키로 만든 복합 기본 키가 있다는 것을 기억하십시오.

이제 두 개의 외래 키는 기본 키의 일부가 아닙니다.

4.2. JPA에서 구현

course_registration 이 일반 테이블 되었으므로 이를 모델링하는 일반 이전 JPA 엔티티를 만들 수 있습니다.

@Entity
class CourseRegistration {

    @Id
    Long id;

    @ManyToOne
    @JoinColumn(name = "student_id")
    Student student;

    @ManyToOne
    @JoinColumn(name = "course_id")
    Course course;

    LocalDateTime registeredAt;

    int grade;
    
    // additional properties
    // standard constructors, getters, and setters
}

또한 StudentCourse 클래스 의 관계를 구성해야합니다 .

class Student {

    // ...

    @OneToMany(mappedBy = "student")
    Set<CourseRegistration> registrations;

    // ...
}

class Course {

    // ...

    @OneToMany(mappedBy = "courses")
    Set<CourseRegistration> registrations;

    // ...
}

다시 말하지만, 관계를 이전에 구성 했으므로 해당 구성을 찾을 수있는 위치 만 JPA에 알리면됩니다.

또한이 솔루션을 사용하여 이전의 학생 평가 과정 문제를 해결할 수 있습니다. 그러나 꼭 필요한 경우가 아니면 전용 기본 키를 만드는 것이 이상합니다.

또한 RDBMS 관점에서 두 개의 외래 키를 결합하여 완벽한 복합 키를 만들었 기 때문에 의미가 없습니다. 게다가, 그 복합 키는 우리가 관계에서 어떤 엔티티를 연결하는지라는 명확한 의미를 가지고있었습니다.

그렇지 않으면이 두 가지 구현 사이의 선택은 종종 단순히 개인적인 선호입니다.

5. 결론

이 기사에서는 다 대다 관계가 무엇이며 JPA를 사용하여 RDBMS에서 어떻게 모델링 할 수 있는지 살펴 보았습니다.

JPA에서 모델링하는 세 가지 방법을 보았습니다. 세 가지 모두 다음과 같은 측면에서 장점과 단점이 다릅니다.

  • 코드 명확성
  • DB 선명도
  • 관계에 속성을 할당하는 기능
  • 관계와 연결할 수있는 엔티티 수
  • 동일한 엔터티 간의 다중 연결 지원

평소처럼 예제는 GitHub에서 사용할 수 있습니다 .