DEV ℧ Developer Diary

[JPA] 다양한 연관관계 매핑 (2)

해당 포스트는 인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 을 듣고 정리한 글입니다.

다양한 연관관계 매핑 (2)

일대일 (1:1)

  • 일대일 관계는 그 반대도 일대일
  • 주 테이블이나 대상 테이블 중에 외래 키 선택 가능 (기존 다의 관계에 들어가야할 외래키의 종속이 없다. 둘다 1의 관계라 가능)
    • 주 테이블 외래키
    • 대상 테이블 외래키
  • 외래 키에 데이터베이스 유니크(UNI) 제약조건 추가

일대일: 주테이블에 외래키 단방향

다대일

해당 관계는 하나의 멤버는 하나의 락커를 쓸 수 있다는 매핑을 보여준다.

양방향으로 만든다면 락커에 멤버의 외래키를 추가만 해주면 된다.

일대일: 주 테이블에 외래키 양방향

  • 다대일 양방향 매핑 처럼 외래 키가 있는 곳이 연관관계의 주인
  • 반대편은 mappedBy 적용 (읽기전용)

일대일: 대상 테이블에 외래키 단방향

Member를 연관관계의 주인으로 하려고 하지만 외래키가 Locker에 있을 경우 단방향에서는 JPA에서 지원이 되지 않는다.

일대일

하지만 양방향으로는 가능하다. mapperBy를 이용해 양방향 처리를 해주면 된다.

정리

  • 주 테이블에 외래 키
    • 주 객체가 대상 객체의 참조를 가지는것처럼 주 테이블에 외래키를 두고 대상테이블을 조회
    • 객체지향 개발자가 선호
    • 장점 : 주 테이블만 조회해서 대상 테이블에 데이터가 있는지 확인가능
    • 단점 : 값이 없으면 외래 키에 null 허용
  • 대상 테이블에 외래 키
    • 대상 테이블에 외래키가 존재
    • 전통적인 데이터베이스 개발자 선호
    • 장점 : 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
    • 단점 : 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시로딩 됨

주의 해야할 점

일대일 관계에서 외래키를 설계할 때 위의 예시가 Member가 여러개의 Locker를 가지게 된다던지 반대가 되는 경우에 대해 일대다 다대일과 같이 변결 될 수 있으니, 설계에 주의 해야한다.

다대다 (N:N)

결론부터 말하자면, 실무에서는 절대 사용하면 안될 연관관계 매핑이다. 왜인지 같이 알아보도록 하자

  • @ManyToMany 사용
  • @JoinTable로 연결 테이블지정
  • 단방향, 양방향 가능

이렇게만 보면 다른 연관관계 매핑과 동일하다. 하지만?

  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
  • 연결 테이블을 주가해서 일대다, 다대일 관계로 풀어내야한다.

일대일

다대다의 딜레마

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

객체가 지원하기때문에 ORM인 JPA의 입장에서는 해당 다대다 관계를 만들었다.

/* Member Entity */
@Entity
@Getter @Setter
public class Member {

    @Id @GeneratedValue
    private long id;

    @Column(name = "USERNAME")
    private String username;

    @ManyToMany
    @JoinColumn(name = "MEMBER_PRODUCT", insertable = false, updatable = false)
    private List<Product> product;
}
/* Product Entity */
@Entity
@Getter @Setter
public class Product {

    @Id @GeneratedValue
    private Long id;

    private String name;
}

수없이 생성되는 제약조건과 테이블들…

    create table Member (
       id bigint not null,
        USERNAME varchar(255),
        primary key (id)
    )

    create table Member_Product (
       Member_id bigint not null,
        product_id bigint not null
    )

    create table Product (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )

    alter table Member_Product
       add constraint FKilnr7a1g687t0svjediiw44p1
       foreign key (product_id)
       references Product

    alter table Member_Product
       add constraint FKhoq1biduawpfxe9ccd0f8mknq
       foreign key (Member_id)
       references Member

한계

  • 편리해 보이지만 실무에서는 사용 X
  • 연결 테이블이 단순히 연결만 하고 끝나지 않음
  • 중간 테이블에는 주문시간, 수량 같은 데이터를 넣을 수 없어서 실무에서 사용하기가 힘들다.
  • 조회를 할 경우 조인 쿼리가 이상하게 생성되서 날라감

다대다 한계 극복

  • 연결 테이블용 엔티티 추가 (연결 테이블을 엔티티로 승격)
  • @MenyToMany -> @OneToMany, @ManyToOne

다대다

연관관계에 관한 팁

연관관계를 맺을때 ID값은 memberId나 email같은 의미있는 데이터 말고 @Id, @GeneratedValue 와 같은 의미없는 값을 PK로 삼는것이 좋다. 비지니스적으로 조건이 추가되거나, 로직이 변경 될 경우 의미있는 값을 PK로 쓰면 크게 구조나 설계가 변경 될 수 있다.

정리하자면 좀더 유연성을 가지고 개발을 할 수 있다.

N:M 관계는 1:N, N:1로

  • 테이블의 N:M 관계는 중간 테이블을 이용해서 1:N, N:1
  • 실전에서는 중간 테이블이 단순하지 않다.
  • @ManyToMany는 제약 : 필드 추가 X, 엔티티 테이블 불일치
  • 실전에서는 @ManyToMany 사용 X