티스토리 뷰



다대다[N:M]

  • 실무에선 사용하지 않는 것을 추천한다. 사용하면 안되는 이유를 학습하자.

  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.

  • 연결 테이블(조인 테이블)을 추가해서 일대다, 다대일 관계로 풀어내야한다.

  • 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계가 가능하다.

    • ORM 입장에서는 테이블은 안되고, 객체는 안되는 것을 지원해줘야 한다.

    • 따라서, 아래의 그림에서와 같이 객체의 다대다 관계(멤버와 프로덕트가 서로 리스트를 가짐)와

    • 테이블에서 다대다 관계를 일대다 다대일 관계로 풀어낸 것 두개의 차이를 연결해준다.

  • JPA @ManyToMany 어노테이션을 사용하고

  • @JoinTable로 연결 테이블을 지정해줄 수 있다.

다대다 단방향

@Entity
public class Member {
  ...
   
   @ManyToMany
   @JoinTable(name = "member_product")
   private List<Product> products = new ArrayList<>();
   
  ...
}
  • 실행된 SQL

    • 조인 테이블이 하나 생성되고, 외래 키 제약조건도 두가지가 설정 된다.

        ...
     
    Hibernate:
       
       create table member_product (
          Member_id bigint not null,
          products_id bigint not null
      )
       
      ...
       
    Hibernate:
       
       alter table member_product
          add constraint FK17rh8i9jrsy7yqy2j6e9yijuw
          foreign key (products_id)
          references Product
    Hibernate:
       
       alter table member_product
          add constraint FK3cjijenmv5sgu1w04p4ofy6ik
          foreign key (Member_id)
          references Member

다대다 양방향

  • 똑같이 @ManyToMany 설정을 해주고, mappedBy 설정을 해줘야 한다.

@Entity
public class Product {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   private String name;

   @ManyToMany(mappedBy = "products")
   private List<Member> members = new ArrayList<>();
}

다대다 매핑의 한계

  • 편리해 보이지만 실무에서 사용하면 안된다.

  • 개발하다 보면, 연결 테이블이 단순히 연결만 하고 끝나지 않는다. 조인 테이블 자체에 주문시간, 수량 같은 추가 데이터가 많이 들어갈 수 있다.

  • 하지만, 매핑 정보만 넣는 것이 가능하고, 추가 정보를 넣는 것 자체가 불가능하다.

  • 그리고 중간 테이블이 숨겨져 있기 때문에 예상하지 못하는 쿼리들이 나간다.

  • 이런 문제점들 때문에 실무에서는 안쓰는게 맞다고 본다.

다대다 한계 극복

  • 어떻게 한계를 극복할까.

  • 연결 테이블용 엔티티를 추가한다. 사실상 연결 테이블을 엔티티로 승격시킨다.

  • 그리고 @ManyToMany를 각각 일대다, 다대일로 관계를 맺어준다.

    • 사실 개인적으로 이부분에 대해서는 @ManyToOne, 다대일 관계 두개로 풀어낸다는 표현이 맞는 것 같다.

    • 앞서 일대다 관계를 학습할 때, 결론적으로 다대일 양방향 매핑을 사용하자는 결론을 내기도 했고,

    • 실제로 아래의 코드상으로 봐도 FK 2개를 중간테이블에서 관리하고, @ManyToOne 양방향 매핑 2개로 이어져있다.

    • 테이블의 배치 순서상(Member -> MemberProduct(Order) -> Product) 표현을 @OneToMany, @ManyToOne으로 표현했던 것이라면 이해가 간다.

  • JPA가 만들어주는 숨겨진 매핑테이블의 존재를 바깥으로 꺼내는 것이다.

  • 위에 다대다 매핑의 한계 첨부 그림에서는 MemberProduct의 MEMBER_ID, PRODUCT_ID를 묶어서 PK로 썻지만, 실제로는 아래 처럼 독립적으로 generated되는 id를 사용하는 것을 권장한다. ID가 두개의 테이블에 종속되지 않고 더 유연하게 개발 할 수 있다. 시스템을 운영하면서 점점 커지는데 만약 비즈니스적인 제약 조건이 커지면 PK를 운영중에 업데이트 하는 상황이 발생할 수도 있다.

  • 코드로 이해하기

    • Member

      • 멤버 엔티티에서 @OneToMany 관계로 변경한다.

        @Entity
        public class Member {
          ...
               
           @OneToMany(mappedBy = "member")
           private List<MemberProduct> memberProducts = new ArrayList<>();

          ...
        }
    • Product

      • 마찬가지로 @OneToMany 관계로 변경한다.

        @Entity
        public class Product {

          ...

           @OneToMany(mappedBy = "product")
           private List<MemberProduct> members = new ArrayList<>();
           
          ...
        }
    • MemberProduct

      • 연결 테이블을 엔티티로 승격시킨다. 그리고 @ManyToOne 매핑을 두개 한다.(연관관계의 주인)

      • 여기서 추가 데이터가 들어간다면 아예 의미있는 엔티티 이름(Order)으로 변경 될 것이다.

        @Entity
        @Getter
        @Setter
        public class MemberProduct {

           @Id
           @GeneratedValue(strategy = GenerationType.IDENTITY)
           private Long id;

           @ManyToOne
           @JoinColumn(name = "member_id")
           private Member member;

           @ManyToOne
           @JoinColumn(name = "product_id")
           private Product product;
        }







댓글
  • 프로필사진 안녕하세요 안녕하세요! 너무 좋은 글 잘 봤습니다. M:N관계에 대해 이해하는 데 큰 도움이 되었습니다!! 감사합니다.

    그런데 질문하고 싶은 부분이 있습니다...! 아래 다대다 한계 극복 부분에서 @ManyToMany 어노테이션을 쓰지 않았을 뿐이지, 결국 Member와 Product는 Order를 통해 M:N관계가 되는 것 아닌가요?? 결국 다대다 형식을 쓰는것 같아서요...그래서 실무에서 다대다 관계를 아예 쓰지 않는 것 보다는, 연결 테이블과 @OneToMany를 사용해서 @ManyToMany를 대체하는 것이 좋다고 내용을 이해했는데, 제가 잘 이해한게 맞나요? 아니면 이조차도 지양하는게 맞나요...? 아니면 연결테이블과 @OneToMany방식을 사용하게되면 Member와 Product가 M:N관계라 할 수 없게 되는건가요??
    2020.06.22 17:01
  • 프로필사진 nroo nroo 안녕하세요 :) 확인을 지금했네요ㅠ

    @ManyToMany 의 한계를 극복하기 위해서 연결 테이블을 엔티티로 승격시키는 부분이 궁금하신 것 같은데요.
    연결테이블을 사용하면 다대일 매핑 두개로 관계가 맺어지기 때문에 다대다 관계는 아닌 것이 됩니다.
    2020.08.11 16:47 신고
댓글쓰기 폼