본문 바로가기

JPA

[JPA] 고급 매핑 - 상속 관계, 복합키와 식별 관계

1. 상속관계 매핑

많이 사용되는 상속관계 매핑에는 3가지 종류가 있다.

 

- 조인 전략

- 단일 테이블 전략

- 자식 테이블에게 매핑 정보만 제공

- 조인 전략

조인 전략은 엔티티 각각을 테이블로 만들고 자식 엔티티가 부모 엔티티의 기본키를 받아서 기본키 + 외래키로 이용하는 전략이다.

테이블을 조회할때, Join을 이용해 상속관계를 표현한다.

 

@Entity
@Inheritance(startegy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public class Entity1{
    @ID
    @GeneratedValue
    @Column(name = "ENTITY_1_ID")
    private Long id;
}

@Entity
@DiscriminatorValue("E2")
public class Entity2 extends Entity1{
    
    /*
    ...
    */
    
}

@Inheritance 는 상속관계에서 선택한 전략을 나타낸다.

위 코드에서는 조인 전략을 사용하고 있으므로, startegy = InteheritanceType.JOINED가 된다.

 

@DiscriminatorColumn 은 부모 클래스에서 구분 컬럼을 지정한다. 기본값은 DTYPE으로 생략해도 된다.

 

@DiscriminatorValue는 부모의 구분 컬럼에 저장할 구분 값을 나타낸다. 

 

조인컬럼은 테이블이 정규화되고, 저장공간을 효율적으로 사용할 수 있지만, 조회시 JOIN이 많이 사용되므로 성능이 저하되고, 데이터 등록시 INSERT문을 항상 두번 이상 실행하는 단점이 있다.

 

- 단일 테이블 전략

단일 테이블 전략은 하나의 테이블에 모든(모든 자식 엔티티의) 필드를 저장하고, 구분 컬럼을 이용해 어떤 자식 데이터가 저장되어 있는지 구분한다. 

@Entity
@Inheritance(startegy = InheritanceType.SINGLE_TABLE)
@DiscirminatorColumn(name = "DTYPE")
public class Entity1{

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

}

@Entity
@DiscriminatorValue("E2")
public class Entity2 extends Entity1{

    /*
    ...
    */
}

단일 테이블전략에서는 

@Inheritance 의 전략을 InheritanceType.SINGLE_TABLE로 설정한다.

 

단일 테이블전략은 일반적으로 JOIN문을 사용하지않으므로 가장 빠르다.

하지만, 하나의 테이블에 모든 필드를 저장하므로 테이블이 커질 수 있고, 상황에 따라서 조회 성능이 더 느릴 수 있다.

 

- 자식테이블에게 매핑 정보만 제공

앞서 본 두가지 전략과는 다르게 이 방법을 사용하면, 부모 테이블은 데이터베이스에 저장되지않고, 부모테이블을 상속받은 자식 테이블만 데이터 베이스에 저장된다.

@MappedSuperclass
public class DateBaseEntity{

    @Column(name = "LAST_MODIFIED_DATE")
    private LocalDate lastModifiedDate;
    
    @Column(name = "CREATED_DATE")
    private LocalDate createdDate;
    
}

@Entity
@AttributeOverrides({
    @AttributeOverride(name = "lastModifiedDate", column = @Column(name = "LMD")),
    @AttributeOverride(name = "createdDate", column = @Column(name = "CD"))
})
@Table(name = "ENTITY1")
public class Entity1 extends DateBaseEntity{
    
    @ID
    @GeneratedValue
    @Column(name = "ENTITY_1_ID")
    private Long id;
    
}

위 코드에서 Entity1은 DateBaseEntity의 필드를 모두 상속받는다. 실제로 실행해보면 Entity1에 DateBaseEntity의 칼럼들이 저장되어 있는것을 볼 수 있다.

 

@AttributeOverrides, @AttributeOverride는 상속받은 (@MappedSuperclass)안의 필드의 칼럼들을 재정의 할때 사용하며, 사용하지않을시 부모 클래스의 칼럼명 그대로 테이블에 저장된다.

2. 복합키와 식별 관계 매핑

JPA에서 두개 이상의 기본키를 사용하려면, 오류가 발생한다. 이 경우, @IdClass 혹은 @EmbeddeId와 같은 방법을 사용해야한다.

 

식별관계 매핑은 식별관계와 비식별 관계 로 나뉜다.

- 식별관계 : 식별 관계는 부모 테이블의 기본 키를 내려받아 자식테이블에서 기본키 + 외래키로 사용하는것을 말한다.

식별관계 매핑의 경우 자식 테이블의 컬럼 갯수가 계속해서 늘어난다는점, 구현이 까다롭다는점, 비즈니스적으로 의미없는 대리키를 사용하기 힘들다는점 때문에, 비 식별관계를 선호하며, 이 글에서는 비식별 관계에 대해서만 적도록 하겠다.

 

- 비식별 관계 : 비식별 관계는 부모 테이블의 기본 키를 받아서 자식 테이블의 외래키로만 사용한다.

또한, 비 식별관계는 필수적 비식별 관계와 선택적 비식별 관계로 나뉘는데, 각각, null값을 허용 여부를 뜻하며, null값을 허용하는 선택적 비식별 관계는 부모 테이블과 연관관계를 맺을지 말지 선택할 수 있다.

 

- 비식별 관계

- @IdClass

 

@Entity
@IdClass(TestIdClass.class)
public class Entity1{
    
    @ID
    @Column(name = "ID1")
    private Long id1;
    
    @ID
    @Column(name = "ID2")
    private Long id2;

}

@Entity
public class Entity2{
    
    @Id
    private Long id;
    
    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "ID1"),
        @JoinColumn(name = "ID2")
    })
    private Entity1 entity1;
    
}

public class TestIdClass implements Serializable{

    private Long id1;
    private Long id2;
    
    public TestIdClass(){}
    
    public TestIdClass(Long id1, Long id2){
        this.id1 = id1;
        this.id2 = id2;
    }
    
    @Override
    public boolean equals(Object o){...}
    
    @Override
    public int hashCode(){...}

}

TestIdClass를 사용하기 위해선, 복합키의 대상이 되는 클래스가 Serializable, equals, hashCode를 구현해야하며, 기본 생성자가 있어야한다.

 

Entity2를 보면, 부모키가 복합키이므로, 자식키도 @JoinColumns를 이용해 복합키로 매핑된것을 볼 수 있다.

 

-@EmbeddedId

@IdClass는 엔티티에 복합키를 매핑했다면, @EmbeddedId는 필드에서 복합키를 그대로 사용한다.

@Entity
public class Entity1{

    @EmbeddedId
    private TestIdClass id;
    
    /*...*/

}


@Embeddable
public class TestIdClass implements Serializable{

    @Column(name = "ID1")
    private String id1;
    
    @Column(name = "ID2")
    private String id2;
    
    // @IdClass와 똑같다.
    
}

EmbeddedId 복합키 매핑 방식을 사용하기 위해선, 복합키가 될 대상 클래스에 @Embeddable 어노테이션이 붙어 있어야하며, IdClass와 마찬가지로, Serializable, hashCode, equals를 구현해야한다.

 

 

 

 

'JPA' 카테고리의 다른 글

[JPA] SpringDataJPA - OSIV  (0) 2021.12.05
[JPA] 지연로딩과 영속성전이  (0) 2021.10.23
[JPA] 연관관계 매핑  (0) 2021.10.14
[JPA] 필드와 컬럼 매핑  (0) 2021.10.12
[JPA] JPA 영속성 컨텍스트  (0) 2021.10.11