본문 바로가기
  • 실행력이 모든걸 결정한다
Spring 사전 준비/JPA Hibernate

[JPA] 고급 매핑 기술

by 김코더 김주역 2022. 7. 28.
반응형

1. 객체의 상속 관계 매핑

- 객체의 상속 구조와 DB의 슈퍼타입-서브타입 관계를 매핑하는 것이다.

- 부모 엔티티를 조회하면 그 자식 엔티티도 함께 조회된다.

 

1) 조인 전략

(1) 특징

- 엔티티들을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 그대로 받아서 기본 키이자 외래 키로 사용하는 전략이다.

- @Inheritance의 strategy 속성을 InheritanceType.JOINED로 지정하면 된다.

 

(2) 장점

- 테이블이 정규화된다.

- 외래 키 참조 무결성 제약조건을 활용할 수 있다.

 

(3) 단점

- JOIN이 많이 사용되기 때문에 복잡하고 조회 성능이 저하될 수 있다.

- 데이터를 등록하는 INSERT문이 2번 실행된다.

 

(4) 예시

- 부모 테이블 ITEM이 자식 테이블 BOOK, MOVIE를 가진다고 가정하자.

<Item.class>

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="DTYPE")
public abstract class Item {

    @Id
    @GeneratedValue
    @Column(name="ITEM_ID")
    private Long id;
    
    private String name;
    private int price;
    // ...
}

- @Inheritance은 상속 매핑을 할 때 부모 클래스에 붙이는 어노테이션이다.

- @DiscriminatorColumn은 자식 테이블을 구분하는 컬럼을 지정하는 어노테이션이다. 컬럼명(name)의 기본값은 "DTYPE"이다.

 

<Book.class>

@Entity
@DiscriminatorValue("B")
public class Book extends Item {
    
    private String author;
    // ...
}

- @DiscriminatorValue는 부모 테이블의 구분 컬럼이 식별할 값을 지정하는 어노테이션이다. 이 예제에서는 부모 테이블의 DTYPE에 "B"가 저장된다.

- 자식 테이블은 기본적으로 부모 테이블의 ID 컬럼명을 그대로 사용한다. 만약에 자식 테이블의 기본 키 컬럼명을 변경하고 싶다면 엔티티 클래스에 @PrimaryKeyColumn을 추가해서 name 속성에 기본 키 컬럼명을 지정해주면 된다.

 

<Movie.class>

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
    
    private String name;
    private String director;
    // ...
}

 

 

2) 단일 테이블 전략

(1) 특징

- JOIN을 사용하지 않고 테이블 하나만 사용하는 전략

- 자식 엔티티가 매핑한 컬럼은 모두 null을 허용하며, 구분 컬럼은 필수다.

- @Inheritance의 strategy 속성을 InheritanceType.SINGLE_TABLE로 지정하면 된다.

- @DiscriminatorValue를 지정하지 않으면 기본적으로 엔티티 이름을 사용한다.

 

(2) 장점

- JOIN이 사용되지 않으므로 단순하고 조회 성능이 좋다.

 

(3) 단점

- 테이블이 너무 커지면 상황에 따라 조회 성능이 느려질 수도 있다.

 

 

3) 구현 클래스마다 테이블을 두는 전략

(1) 특징

- 자식 엔티티마다 테이블을 만들고, 자식 테이블 각각에 필요한 컬럼을 모두 넣는 전략이다.

- 구분 컬럼이 없다.

- @Inheritance의 strategy 속성을 InheritanceType.TABLE_PER_CLASS로 지정하면 된다.

- 추천하지 않는 전략이다.

 

(2) 장점

- 서브 타입을 구분해서 처리하기 좋다.

- not null 제약조건을 사용할 수 있다.

 

(3) 단점

- UNION을 사용하기 때문에 여러 자식 테이블을 한꺼번에 조회할 때 성능이 안좋다.

- 자식 테이블끼리 통합하기 불편할 수 있다.

 

 

 

2. @MappedSuperClass

- 부모 클래스에 @Entity 대신 @MappedSuperClass를 붙이면 부모 클래스는 테이블과 매핑하지 않고, @Entity가 붙은 자식 클래스는 부모 클래스에게 매핑 정보만 상속받아서 매핑시킬 수 있다. @MappedSuperClass를 붙은 클래스는 실제 테이블과 매핑되지 않는다.

@MappedSuperclass
public abstract class Base {
    @Id
    @GeneratedValue
    private Long id;
    
    private String name;
    // ...
}

@Entity
public class Member extends Base {
    // ID 상속
    // NAME 상속
    private String phone;
    // ...
}

※ @Entity 클래스는 @Entity 또는 @MappedSuperclass 클래스만 상속받을 수 있다.

※ 부모 클래스로부터 물려받은 매핑 정보를 @AttributeOverride 또는 @AttributeOverrides를 사용하여 재정의할 수도 있다.

 

 

 

 

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

1) 식별 관계와 비식별 관계

- 식별 관계는 부모 테이블의 기본 키를 자식 테이블이 기본 키이자 외래 키로 활용하는 관계이고, 비식별 관계는 부모 테이블의 기본 키를 자식 테이블이 외래 키로만 사용하는 관계다.

- 식별 관계는 기본 키를 자식 테이블로 전파하기 때문에, 하위 자손 테이블로 갈수록 기본 키 컬럼이 점점 늘어난다. 그래도 하위 테이블만으로도 상위 테이블의 기본 키를 조건으로 검색할 수 있다는 장점이 있기는 하다.

- 데이터베이스 설계 관점에서든 객체 관계 매핑에서든 비식별 관계를 선호하는 편이다.

 

 

 

2) @IdClass 방식

- 아래 포스팅에서 [2-3)복합 키를 갖는 연결 엔티티 방식]을 참고하자.

https://kimcoder.tistory.com/485?category=964983

 

[JPA] 일대일 매핑과 다대다 매핑

1. 일대일 매핑 1) 특징 - 둘 중 어느 곳에나 외래 키를 가질 수 있다. - 객체 매핑에 @OneToOne 어노테이션을 사용하고 DB의 외래 키에는 유니크 제약 조건을 건다. - 예시로 직원과 사물함의 관계가

kimcoder.tistory.com

 

 

 

3) @EmbeddedId 방식

- @IdClass가 DB에 맞춘 방식이라면 @EmbeddedId는 객체지향적인 방식이다.

- 예제를 통해 식별자 클래스를 이해해보자.

 

(1) 비 식별 관계 매핑

<1> 식별자 클래스 생성 및 참조

- 식별자 클래스에 @Embeddable을 붙여주고, 식별자 클래스를 참조하는 엔티티의 필드에는 @EmbeddedId를 붙여주면 된다. 그 외의 식별자 클래스의 조건은 @IdClass 방식과 동일하다.

- @IdClass 방식과 달리 식별자 클래스에 기본 키를 직접 매핑한다.

@EqualsAndHashCode
@Embeddable
public class ParentId implements Serializable {

    @Column(name="PARENT_ID1")
    private String id1;
    @Column(name="PARENT_ID2")
    private String id2;
    
    // ...
}

 

@Entity
public class Parent {
    
    @EmbeddedId
    private parentId id; // 기본 키를 직접 매핑
    
    // ...
}

 

<2> 식별자 클래스 사용

ParentId parentId = new ParentId("id1", "id2");
Parent parent = new parent();
parent.setId(parentId); // 식별자 클래스 객체를 넣는다.
parent.setName("jpa");
em.persist(parent);

 

<3> 식별자 클래스 조회

Parent parent = em.find(Parent.class, parentId); // id 파라미터에 식별자 클래스 객체를 넣는다.

 

 

(2) 식별 관계 매핑

- @MapsId는 외래 키와 매핑한 연관관계를 기본 키에도 매핑해주는 어노테이션이다.

- 식별 관계로 사용할 연관관계의 속성에 @Id 대신 @MapsId를 사용하면 된다. @MapsId의 속성 값은 식별자 클래스의 기본 키 필드로 지정한다.

 

<Child.class>

- CHILD는 CHILD_ID, PARENT_ID를 모두 기본 키로 가지는 식별 관계이다. 물론 PARENT_ID는 외래 키로도 활용한다.

@Entity
public class Child {

    @EmbeddedId
    private ChildId id;

    @MapsId("parentId")
    @ManyToOne
    @JoinColumn(name="PARENT_ID")
    public Parent parent;
    
    // ...
}

 

<ChildId.class>

@EqualsAndHashCode
@Embeddable
public class ChildId implements Serializable {
    
    private String parentId; // Child.class의 @MapsId("parentId")로 매핑된다.
    
    @Column(name="CHILD_ID")
    private String id;
    
    // ...
}

 

 

 

 

4. Join 테이블

- 연관관계를 맺기 위해 외래 키 또는 연결 테이블을 사용할 수 있다.

- 연결 테이블, 링크 테이블로도 부른다.

 

1) 일대일 조인 테이블

- 부모 엔티티쪽에 @OneToOne, @JoinTable 어노테이션을 사용해서 조인 테이블을 추가한다.

 

 

2) 일대다 조인 테이블

- 부모 엔티티쪽에 @OneToMany, @JoinTable 어노테이션을 사용해서 조인 테이블을 추가한다.

 

 

3) 다대일 조인 테이블

- 자식 엔티티쪽에 @ManyToOne, @JoinTable 어노테이션을 사용해서 조인 테이블을 추가한다.

 

 

4) 다대다 조인 테이블

- 부모 엔티티쪽에 @ManyToMany, @JoinTable 어노테이션을 사용해서 조인 테이블을 추가한다.

- 아래 포스팅의 [2-2)@ManyToMany 방식]를 참고할 것

https://kimcoder.tistory.com/485

 

[JPA] 일대일 매핑과 다대다 매핑

1. 일대일 매핑 1) 특징 - 둘 중 어느 곳에나 외래 키를 가질 수 있다. - 객체 매핑에 @OneToOne 어노테이션을 사용하고 DB의 외래 키에는 유니크 제약 조건을 건다. - 예시로 직원과 사물함의 관계가

kimcoder.tistory.com

 

 

 

5. 엔티티 하나에 여러 테이블 매핑하기

- 잘 사용되는 방법은 아니기 때문에 @SecondaryTable을 이용한다는 것만 알아두자.

 

 

 

● 참고자료 : 자바 ORM 표준 JPA 프로그래밍

반응형

댓글