Home 고급 매핑
Post
Cancel

고급 매핑

상속 관계 매핑

  • 관계형 데이터베이스에는 객체지향 언어에서 다루는 상속이라는 개념이 없다.
  • 슈퍼타입 서브타입 관계라는 모델링 기법이 객체의 상속 개념과 가장 유사하다.
  • ORM에서 이야기하는 상속 관계 매핑은 객체의 상속 구조와 데이터베이스의 슈퍼타입 서브타입 관계를 매핑하는 것이다.
  • 슈퍼타입 서브타입 논리 모델을 실제 물리 모델인 테이블로 구현할 때는 3가지 방법이 있다.

조인 전략 매핑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public class Item {
  @Id
  @GeneratedValue
  @Column(name = "ITEM_ID")
  private Long id;

  private String name;
  private String price;


}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
  private String artist;
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {...
}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {...
}
  • 위와 같이 구현할 수 있다.
  • 장점 :
    • 테이블이 정규화 되고 저장공간을 효율적으로 사용한다.
    • 외래키 참조 무결성 제약조건을 활용할 수 있다.
  • 단점 :
    • 조회할 때 조인이 많아 쿼리가 복잡하고 성능이 저하될 수 있다.
    • 데이터를 등록할 INSERT SQL을 두 번 실행한다.
  • 특징 :
    • JPA 표준 명세는 구분 컬럼을 사용하도록 하지만 하이버네이트를 포함한 몇몇 구현체는 구분 컬럼(@DiscriminatorColumn)없이도 동작한다.

단일 테이블 전략

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
  @Id
  @GeneratedValue
  @Column(name = "ITEM_ID")
  private Long id;
  private String name;
  private String price;
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {...
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {...
}
  • 위와 같이 구현할 수 있다.
  • 장점 :
    • 조인이 필요없어 조회 성능이 빠르고 쿼리가 단순하다.
  • 단점 :
    • 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 한다.
    • 단일 테이블에 모든 것을 저장해서 테이블이 커져 오히려 조회 성능이 느려질 수 있다.
  • 특징 :
    • 구분 컬럼(@DiscriminatorColumn)을 꼭 사용해야 한다.

구현 클래스마다 테이블 전략

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
  @Id
  @GeneratedValue
  @Column(name = "ITEM_ID")
  private Long id;
  private String name;
  private String price;
}

@Entity
public class Album extends Item {...
}

@Entity
public class Movie extends Item {...
}
  • 위와 같이 구현할 수 있다.
  • 일반적으로 추천하지 않는 전략이다
  • 장점 :
    • 서브 타입을 구분해서 처리할 때 효과적이다.
    • not null 제약조건을 사용할 수 있다.
  • 단점 :
    • 여러 자식 테이블을 함께 조회할 때 성능이 느리고 자식 테이블을 통합해서 쿼리하기가 어렵다.
  • 특징 :
    • 구분 컬럼을 사용하지 않는다.

@MappedSuperclass

  • @MappedSuperclass는 부모 클래스를 테이블과 맵핑하지 않고 자식 클래스만 맵핑을 하고 싶을 때 사용한다.
  • 간단하게 추상 클래스와 비슷하다고 생각하면 된다.

  • 사용 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@MappedSuperclass
public class BaseEntity {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  //...
}

@Entity
public class Member extends BaseEntity {
  private String email;
}

@Entity
public class Seller extends BaseEntity {
  private String shopName;
}
  • 추가적으로 부모로부터 물려받은 매핑 정보를 재정의 할려면 @AttributeOverrides, @AttributeOverride
  • 연관관계를 재정의하려면 @AssociationOverrides, @AssociationOverride

복합 키와 식별 관계 매핑

비식별 관계 - @IdClass

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
@IdClass(ParentId.class)
public class Parent {
  @Id
  @Column(name = "PARENT_ID1")
  private String id1;

  @Id
  @Column(name = "PARENT_ID2")
  private String id2;
  private String name;
}
  • 위와 같이 Parent 엔티티를 작성할 수 있다.
  • 여기서 Parent는 복합 키로 이루어져 있기 때문에 별도의 식별자 클래스를 만들어야 한다.
1
2
3
4
5
6
7
@NoArgsConstructor // 기본 생성자
@EqualsAndHashCode // equals , haseCode
public class ParentId implements Serializable {
  private String id1;
  private String id2;
}
  • 위와 같이 @IdClass 에 해당하는 클래스를 만들기 위해서는 아래 조건을 만족해야 한다.
  • 조건 :
    • 식별자 클래스의 속성명과 엔티티에서 사용하는 식발자의 속성명이 같아야 한다.
    • Serializable 인터페이스를 구현해야 한다.
    • equals, hashCode를 구현해야 한다.
      • 자바에서 제공하는 equals, hashCode 는 인스턴스 기준으로 하기 때문에 이를 값 비교로 비교할 수 있도록 적절히 오버라이드 해야한다.
    • 기본 생성자가 있어야 한다.
    • 식별자 클래스는 public 이어야 한다.
1
2
3
4
5
6
7
8
9
10
@Entity
public class Child {
  @Id
  private String id;

  @ManyToOne
  @JoinColumns(@JoinColumn(name = "PARENT_ID1"), @JoinColumn(name = "PARENT_ID1"))
  private Parent parent;
}
  • Child 엔티티 역시 Parent 엔티티의 기본 키 컬럼이 복합 키이므로 자식 테이블의 외래 키도 복합 키로 설정해야하는데 위와 같이 할수 있다.

비식별 관계 - @EmbeddedId

  • @EmbeddedId는 @IdClass 보다 객체지향적인 방법으로 설계할 수 있다.
1
2
3
4
5
6
7
@Entity
public class Parent {
  @EmbeddedId
  private ParentId id;
  private String name;
}
  • Parent 엔티티는 식별자 클래스를 객체 자체를 가지고 있는다.
1
2
3
4
5
6
7
8
9
10
@NoArgsConstructor
@EqualsAndHashCode
@Embeddable
public class ParentId implements Serializable {
  @Column(name = "PARENT_ID1")
  private String id1;
  @Column(name = "PARENT_ID2")
  private String id2;
}
  • 위와 같이 @EmbeddedId를 적용할 식별자 클래스를 만들기 위해서는 아래 조건을 만족해야 한다.
  • 조건 :
    • @IdClass와 다르게 @EmbeddedId를 적용한 식별자 클래스에 기본 키를 맵핑한다.
    • @Embeddable 어노테이션을 붙여야 한다.
    • Serializable 인터페이스를 구현해야 한다.
    • equals, hashCode를 구현해야 한다.
    • 기본 생성자가 있어야 한다.
    • 식별자 클래스는 public 이어야 한다.

식별 관계 - @IdClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@IdClass(ChildId.class)
public class Child {
  @Id
  @Column(name = "CHILD_ID")
  private String id;

  @Id
  @ManyToOne
  @JoinColumns(@JoinColumn(name = "PARENT_ID1"), @JoinColumn(name = "PARENT_ID1"))
  private Parent parent;

  private String name;
}
  • @ManyToOne에 @Id 를 붙여주면 식별관계로 설계할 수 있다.
1
2
3
4
public class ChildId implements Serializable {
  private ParentId parentId;
  private string id;
}
  • Child 엔티티도 Parent 외래 키를 식별키로 사용하기 때문에 별도의 식별자 클래스를 만들어야 한다.

식별 관계 - @EmbeddedId

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class Child {
  @EmbeddedId
  private ChildId id;

  @MapsId("parentId") //ChildId.parentId 매핑
  @ManyToOne
  @JoinColumn(name = "PARENT_ID")
  private Parent parent;

  private String name;
}
  • @MapsId를 통해 외래키와 매핑한 연관관계를 기본 키에 매핑한다.
1
2
3
4
5
6
7
8
9
@NoArgsConstructor
@EqualsAndHashCode
@Embeddable
public class ChildId implements Serializable {
  private ParentId parentId; //@MapsId("parentId")로 매핑
  @Column(name = "CHILD_ID")
  private String id;
}

조인 테이블

  • 사용할 일이 생기면 그때 자세히 다루겠다.

@Inheritance

  • @Inheritance는 상속 매핑을 할때 사용하고 부모 클래스에 사용해야 한다.
속성기능기본값
strategyInheritanceType 설정InheritanceType.SINGLE_TABLE

InheritanceType

  1. InheritanceType.JOINED : 조인 전략
  2. InheritanceType.SINGLE_TABLE : 단일 테이블 전략
  3. InheritanceType.TABLE_PER_CLASS : 구현 클래스마다 테이블 전략

@DiscriminatorColumn

  • @DiscriminatorColumn는 부모 클래스에서 사용하고 구분 컬럼을 지정할 때 사용한다.
속성기능기본값
name구분자 컬럼이름 지정“DTYPE”
discriminatorTypeDiscriminatorType 설정DiscriminatorType.STRING
columnDefinition구분자 컬럼의 정의를 추가로 지정 
length구분자 컬럼의 길이를 지정31

DiscriminatorType

  1. DiscriminatorType.STRING : 구분자 타입의 유형을 String
  2. DiscriminatorType.CHAR : 구분자 타입의 유형을 Char
  3. DiscriminatorType.INTEGER : 구분자 타입의 유형을 Integer

@DiscriminatorValue

  • @DiscriminatorValue는 엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정할때 사용한다.
  • 그렇기 때문에 서브 클래스에 사용한다.
속성기능기본값
value컬럼에 입력할 값 
This post is licensed under CC BY 4.0 by the author.