다대일 [N:1]

외래 키는 항상 다쪽에 있다. 따라서 객체 양방향 관계에서 연관관계의 주인은 항상 다쪽이다.

다대일: 양방향

양방향은 외래 키가 있는 쪽이 연관관계의 주인이다.

  • JPA는 외래 키를 관리할 때 연관관계의 주인만 사용한다.
  • 주인이 아닌 쪽은 조회를 위한 JPQL이나 객체 그래프를 탐색할 때 사용한다.

    양방향 연관관계는 항상 서로를 참조해야 한다.

  • 무한루프에 빠지지 않게 주의해야 한다.

일대다 [1:N]

자바 컬렉션인 Collection, List, Set, Map 중에 하나를 사용해야 한다.

일대다: 단방향

단점

  • 매핑한 객체가 관리하는 외래 키가 다른 테이블에 있다.
  • 다른 테이블에 외래 키가 있으면 INSERT SQL에 추가로 연관관계 처리를 위한 UPDATE SQL을 실행해야 한다.

일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자.

  • 상황에 따라 다르겠지만 일대다 단방향 매핑보다는 다대일 양방향 매핑을 권장한다.

일대다: 양방향

일대다 양방향 매핑이라기보다는 일대다 단방향 매핑 반대편에 다대일 단방향 매핑을 읽기 전용으로 추가해서 일대다 양방향처럼 보이도록 하는 방법이다.

일대다 단방향 매핑이 가지는 단점을 그대로 가진다.

일대일 [1:1]

일대일 관계는 주 테이블이나 대상 테이블 중에 누가 외래 키를 가질지 선택해야 한다.

주 테이블에 외래 키

외래 키를 참조와 비슷하게 사용할 수 있어서 객체지향 개발자들이 선호한다.

주 테이블만 확인해도 대상 테이블과 연관관계가 있는지 알 수 있다.

대상 테이블에 외래 키

전통적인 데이터베이스 개발자들은 보통 대상 테이블에 외래 키를 두는 것을 선호한다.

테이블 관계를 일대일에서 일대다로 변경할 때 테이블 구조를 그대로 유지할 수 있다.

주의

프록시를 사용할 때 외래 키를 직접 관리하지 않는 일대일 관계는 지연 로딩으로 설정해도 즉시 로딩된다. (자세한 내용은 8장에서..)

다대다 [N:N]

관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다. 그래서 보통 다대다 관계를 일대다, 다대일 관계로 풀어내는 연결 테이블을 사용한다.

다대다: 단방향

  ...
  @ManyToMany
  @JoinTable(name = "MEMBER_PRODUCT",
              joinColumns = @JoinColumn(name = "MEMBER_ID"),
              inverseJoinColumns = @JoinColumn(name =
                "PRODUCT_ID"))
  private List<Product> products = new ArrayList<Product>();
  • @JoinTable.name
    • 연결 테이블을 지정한다.
  • @JoinTable.joinColums
    • 현재 방향인 회원과 매핑할 조인 컬럼 정보를 지정한다.
  • @JoinTable.inverseJoinColumns
    • 반대 방향인 상품과 매핑할 조인 컬럼 정보를 지정한다.

MEMBER_PRODUCT 테이블은 다대다 관계를 일대다, 다대일 관계로 풀어내기 위해 필요한 연결 테이블일 뿐이다.

다대다: 양방향

역방향도 @ManyToMany를 사용한다.

다대다: 매핑의 한계와 극복, 연결 엔티티 사용

연결 테이블에 컬럼을 추가하면 더는 @ManyToMany를 사용할 수 없다.

이럴 때는 다대다에서 일대다, 다대일 관계로 풀어야 한다.

회원상품 엔티티 코드
@Entity
@IdClass(MemberProductId.class)
public class MemberProduct {

  @Id
  @ManyToOne
  @JoinColumn(name = "MEMBER_ID")
  private Member member; // MemberProductId.member와 연결

  @Id
  @ManyToOne
  @JoinColumn(name = "PRODUCT_ID")
  private Product product; // MemberProductId.product와 연결

  ...
}
회원상품 식별자 클래스
public class MemberProductId implements Serializable {

  private String member;  // MemberProduct.member와 연결
  private String product; // MemberProduct.product와 연결

  // hashCode and equals

  @Override
  ...
}
  • 이는 식별 관계다.

식별 관계

  • 부모 테이블의 기본 키를 받아서 자신의 기본 키 + 외래 키로 사용하는 것을 데이터베이스 용어로 식별 관계라 한다.

회원상품 엔티티를 보면 기본 키를 매핑하는 @Id와 외래 키를 매핑하는 @JoinColumn을 동시에 사용해서 기본 키 + 외래 키를 한번에 매핑했다. 그리고 @IdClass를 사용해서 복합 기본 키를 매핑했다.

복합 기본 키

  • 회원상품 엔티티는 기본 키가 MEMBER_ID와 PRODUCT_ID로 이루어진 복합 기본키다.
  • JPA에서 복합 키를 사용하려면 별도의 식별자 클래스를 만들어야 한다. 그리고 엔티티에 @IdClass를 사용해서 식별자 클래스를 지정하면 된다.
  • 복합 키를 위한 식별자 클래스의 특징
    • Serializable을 구현해야 한다.
    • equals와 hashCode 메소드를 구현해야 한다.
    • 기본 생성자가 있어야 한다.
    • 식별자 클래스는 public이어야 한다.
    • @IdClass를 사용하는 방법 외에 @EmbeddedId를 사용하는 방법도 있다.

복합 키는 항상 식별자 클래스를 만들어야 한다.

복합 키를 사용하는 방법은 복잡하다.

다음으로 복합 키를 사용하지 않고 간단히 다대다 관계를 구성하는 방법을 알아보자.

다대다: 새로운 기본 키 사용 (비식별 관계)

추천하는 기본 키 생성 전략은 데이터베이스에서 자동으로 생성해주는 대리 키를 값으로 사용하는 것이다.

  • 간편하고 거의 영구히 쓸 수 있으며 비즈니스에 의존하지 않는다.
  • ORM 매핑 시에 복합 키를 만들지 않아도 되므로 간단히 매핑을 완성할 수 있다.

이제 회원상품보다는 주문이라는 이름이 더 어울릴 것이다.(예약어 조심!)

주문 코드
@Entity
public class Order {

  @Id @GeneratedValue
  @Column(name = "ORDER_ID")
  private Long Id;

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

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

  ...
}

+ 따끈한 최근 게시물