Spring Model/03. 연관관계 처리

04. 연관 관계 - M:N 관계

  • -

이번 시간에는 M:N 관계의 처리에 대해 살펴보자.

 

M:N 관계는 양측 모두가 여러 개의 관계를 가지는 경우이다. 예를 들어 게시글과 카테고리를 생각해보면 게시글 하나가 여러 카테고리에 속해있을 수 있고 하나의 카테고리에는 여러 개의 게시글이 있을 수 있다.

Post: Category = M:N

일반적으로 DB에서는 이런 상황을 처리하기 위해 중간에 연계 테이블을 만들어서 N:1 관계로 풀어서 처리하는 것이 일반적이다.

https://goodteacher.tistory.com/466/#comment18561464

 

[DB] 다대다 관계의 문제점과 해결

이번 포스트에서는 다대다 관계의 문제점과 해결 방법에 대해서 살펴보자.  다대다 관계다대다 관계의 예DB 모델링 과정에서 다대다 관계가 나올 수 있다. 쉽게 생각할 수 있는 관계로는 학생과

goodteacher.tistory.com

 

JPA에서는 이를 처리하기 위해 @ManyToMany 애너테이션을 지원한다. 

@Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface ManyToMany { String mappedBy() default ""; CascadeType[] cascade() default {}; FetchType fetch() default FetchType.LAZY; }

사용법도 매우 단순하다. 대상 엔티티 모두에게 @ManyToMany를 추가하고 주엔티티인 Post에 mappedBy 속성을 지정하면 된다.

@Entity public class Category extends BaseEntity{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long cno; private String name; @ManyToMany @Builder.Default @ToString.Exclude private List<Post> posts = new ArrayList<>(); }
@Entity public class Post extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long pno; . . . @ManyToMany(mappedBy = "posts") @ToString.Exclude @Builder.Default private List<Category> categories = new ArrayList<>(); }

 

위와 같은 설정 결과 생성된 테이블의 구조는 다음과 같다.

자동으로 1:N의 관계로 풀어주게 된다.

 

더보기

 

Hibernate: create table category ( cno bigint generated by default as identity, name varchar(255), primary key (cno) ) Hibernate: create table category_posts ( categories_cno bigint not null, posts_pno bigint not null ) Hibernate: create table post ( created timestamp(6), modified timestamp(6), pno bigint generated by default as identity, writer bigint, content varchar(255), title varchar(255), primary key (pno) ) Hibernate: alter table if exists category_posts add constraint FKmx347vmsshx3qxcxeu9ruthrk foreign key (posts_pno) references post Hibernate: alter table if exists category_posts add constraint FK66viirlv9o5bys7a22r4jq1r9 foreign key (categories_cno) references category

즉 자동으로 Category_Posts이라는 연계 테이블을 생성하는데 이 테이블은 Post, Category와 각각 1:N의 관계를 맺게 된다. 연계 테이블의 P.K는 Post와 Category에 대한 F.K를 합쳐서 복합키로 사용한다.

언뜻 보면 매우 간단해보이지만 이렇게 사용할 수 있는 경우는 연계 테이블이 단순히 복합키 만 가지는 경우이다. 즉 Category_Posts에  추가일, 수정일 등 추가적으로 필요한 컬럼들이 있다면 사용할 수 없다.

따라서 M:N의 관계에서는 @ManyToMany에 의해 자동으로 생성되는 연계 테이블을 사용하기 보다연계 테이블과 엔티티를 직접 생성해서 1:N의 관계를 명시적으로 주는 것을 권장한다.

package com.doding.board.post.model.entity; import com.doding.board.common.entity.BaseEnttiy; import jakarta.persistence.*; import java.time.LocalDateTime; @Entity public class CategoryPost extends BaseEnttiy { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long cpno; @ManyToOne @JoinColumn(name = "pno") private Post post; @ManyToOne @JoinColumn(name = "cno") private Category category; }
@Entity public class Category extends BaseEnttiy{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long cno; @OneToMany(mappedBy = "category") @Builder.Default @ToString.Exclude private List<CategoryPost> categoryPosts = new ArrayList<>(); }
@Entity public class Post extends BaseEnttiy { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long pno; @OneToMany(mappedBy = "post") @ToString.Exclude @Builder.Default private List<CategoryPost> categoryPosts = new ArrayList<>(); }

@ManyToOne은 이전 포스트에서 많이 살펴봤기 때문에 추가적인 설명은 생략한다.

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.