자바 ORM 표준 JPA 프로그래밍

http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788960777330

상속 관계 매핑

ORM에서 이야기하는 상속 관계 매핑은 객체의 상속 구조와 데이터베이스의 슈퍼타입 서브타입 관계를 매핑하는 것이다.

매핑 방법 3가지

  1. 조인 전략

각각의 테이블로 변환하는 방법이다.
테이블은 타입 개념이 없으므로 타입을 구분하는 컬럼으로 DTYPE 컬럼을 추가해줘야한다.

부모 클래스에 @Inheritance(strategy = InheritanceType.JOINED) 를 붙여서 부모 클래스이며 조인 전략으로 매핑할 것이라고 명시한다.

DTYPE 컬럼에는 @DiscriminatorColumn(name = "DTYPE")를 붙여서 부모 클래스의 구분 컬럼임을 명시해줘야 한다.
기본 값이 DTYPE 이므로 컬럼명이 DTYPE이면 뒤 괄호는 생략 가능하다.

자식 클래스에 @DiscriminatorValue("M") 을 붙여서 엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정한다.

자식 테이블의 기본 키 이름의 기본 값은 부모 테이블의 기본 키 이름이다.
변경하려면 @PrimaryKeyJoinColumn(name = "movie_id")을 사용하여 변경가능하다.

장점

테이블이 정규화 된다.
외래 키 참조 무결성 제약조건을 활용할 수 있다.???
저장공간을 효율적으로 사용한다.

단점

조회할 때 조인이 많이 사용되어 성능 저하될 수 있다.
조회 쿼리가 복잡해진다.
데이터를 등록할 때 INSERT SQL을 두번 실행한다.

JPA 표준 명세는 구분 컬럼을 사용하도록 한다.
하이버네이트를 포함한 몇몇 구현체는 구분 컬럼 없이도 동작하긴 한다.

  1. 단일 테이블 전략

테이블을 하나만 생성하고 자식 테이블의 컬럼을 모두 부모 테이블에 생성하고 사용하지 않는 컬럼은 null을 넣어두는 방법이다.
DTYPE을 구분 컬럼으로 사용하여 어떤 자식 데이터가 저장되었는지 구분 한다.
테이블은 하나만 생성하지만 클래스는 당연히 자식 클래스는 생성해줘야한다.

부모 클래스에 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 을 붙여서 전략을 명시해줄 수 있다.

DTYPE 컬럼에는 @DiscriminatorColumn(name = "DTYPE")를 붙여서 부모 클래스의 구분 컬럼임을 명시해줘야 한다.

자식 클래스에 @DiscriminatorValue 를 설정해줘야한다.
안해주면 클래스 이름이 기본 값으로 들어간다.

장점

조인이 필요없어 조회 성능이 빠르다.
조회 쿼리가 단순하다.

단점

자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야한다.
테이블이 커져서 오히려 조회 성능이 느려질 수 있다.

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

자식 엔티티마다 테이블을 만들고 거기에 부모 클래스의 값까지 넣는 방식이다.
구분 컬럼을 사용하지 않는다.
데이터베이스 설계자와 ORM 전문가 둘다 추천하지 않는 전략이다.

부모 클래스에 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 을 붙여서 전략을 명시해줄 수 있다.

장점

서브 타입을 구분해서 처리할 때 효과적이다.
not null 제약조건을 사용할 수 있다.

단점

여러 자식 테이블을 함께 조회할 때 성능이 느리다(SQL에 UNION을 사용해야한다.)
자식 테이블을 통합해서 쿼리하기 어렵다.

@MappedSuperclass

부모 클래스는 테이블과 매핑하지 않고 부모 클래스를 상속 받는 자식 클래스에게 매핑 정보만 제공하고 싶을때 사용한다.

BaseEntity 만들때 사용한다.
BaseEntity는 직접 생성해서 사용할 일이 없으므로 추상 클래스로 만드는 것이 좋다.

상속받은 매핑 정보를 재정의하려면 @AttributeOverride 를 사용하면 된다.

복합 키와 식별 관계 매핑

식별 관계

부모 테이블의 기본 키를 내려받아서 자식 테이블의 기본 키로 사용하는 관계이다.

비식별 관계

부모 테이블의 기본 키를 자식 테이블에서 외래 키로만 사용하는 관계이다.

필수적 비식별 관계

외래 키에 null을 허용하지 않는다.
연관관계를 필수적으로 맺어야 한다.

선택적 비식별 관계

외래 키에 null을 허용한다.
연관관계를 선택적으로 맺을 수 있다.

복합 키

JPA에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 한다.

JPA는 영속성 컨텍스트에 보관한 엔티티를 구분하기 위해 식별자에 equals와 hashCode를 사용해서 동등성 비교를 한다.

식별자 필드가 2개 이상이면 별도의 식별자 클래스를 만들고 그곳에 equals와 hashCode를 오버라이드하여 구현해야한다.

@IdClass(ParentId.class)

식별자 필드가 2개 이상인 클래스에 식별자 클래스를 설정해준다.

이때 식별자 클래스는
엔티티 클래스와 식별자 속성명이 같아야한다.
Serializable 인터페이스를 구현해야한다.
equals, hashCode를 구현해야 한다.
기본 생성자가 있어야한다.
public 클래스어야 한다.

사용할 때 엔티티를 생성하여 저장하면 자동으로 식별자 클래스를 생성해준다.

@EmbeddedId

좀 더 객체지향적으로 식별자 클래스를 설정하는 방법이다.

이때 식별자 클래스는
@Embeddable 어노테이션을 붙여주어야 한다.
Serializable 인터페이스를 구현해야한다.
equals, hashCode를 구현해야 한다.
기본 생성자가 있어야한다.
public 클래스어야 한다.

사용할 때 식별자 클래스를 직접 생성하고 이를 이용하여 엔티티를 생성해야한다.

이 방식이 좀 더 객체지향적이고 중복도 없어서 좋지만 특정 상황에 JPQL이 조금 더 길어질 수 있다.????? 어떤 상황??

복합 키를 사용한 식별관계 구현

ManyToOne 관계에서 식별관계로 외래 키를 가지는 엔티티는 식별자 클래스를 작성해야한다.

그러나 이를 비식별 관계로 구현하면 복합 키 없이 구현가능하여 식별자 클래스를 작성하지 않아도 된다.

OneToOne 식별관계의 경우에는 복합키로 구성하지 않아도 되어 식별자 클래스를 작성하지 않아도 된다.

식별 관계 vs 비식별 관계

식별 관계는 부모 테이블의 기본 키를 자식 테이블로 전파하면서 자식 테이블의 기본 키 컬럼이 점점 늘어난다.
때문에 조인할 때 SQL이 복잡해지고 기본 키 인덱스가 불필요하게 커질 수 있다.
ManyToOne이면 복합 기본 키를 만들어야해서 식별자 클래스를 작성해야한다.
비즈니스에 의미있는 자연 키 컬럼의 경우에는 식별 관계를 사용하기도 한다.
비즈니스 요구사항은 변경될 수 있으므로 이런 키 컬럼이 자식에 손자까지 전파되면 변경하기 힘들다.
따라서 식별 관계는 유연하지 못하다.
하지만 부모의 기본 키를 자식이 가지고 있어 조인 없이 검색이 가능하기도 하다.

비식별관계는 객체지향적 관점에서 선호된다.
ManyToOne일때 복합 기본 키를 사용하지 않아도 되어 식별자 클래스를 작성할 필요가 없다.
기본 키를 주로 대리 키를 사용하여 키 생성이 편하다.
대리 키는 비즈니스와 관련이 없어 상대적으로 유연하다.

선택적 비식별관계는 null을 허용하므로 조인할 때 외부 조인을 사용해야한다.
필수적 비식별관계는 내부 조인만 사용해도 되므로 더 좋다.

조인 테이블

데이터베이스 테이블의 연관관계를 설계하는 방법
1. 조인 컬럼 사용
2. 조인 테이블 사용

조인 컬럼

외래 키를 사용하는 방법이다.
두 테이블의 로우 사이에 관계가 없는 경우 외래 키에 null을 사용해야하므로 null을 허용해야할 수 있다.

조인 테이블

별도의 테이블에서 외래 키를 저장하여 관계를 맺는 방법이다.

일대일 조인 테이블

조인 테이블의 외래 키 컬럼에 각각 유니크 제약조건을 걸어야한다.

일대다 조인 테이블

일대다 중 다와 관련된 컬럼을 기본 키로 사용하여 유니크 제약조건을 걸어야한다.

다대다 조인 테이블

조인 테이블의 두 컬럼을 합해서 하나의 복합 유니크 제약조건을 걸어야 한다.
조인 테이블에 컬럼을 추가하면 @JoinTable 전략을 사용할 수 없고 별도의 엔티티를 작성해어 조인 테이블과 매핑해야한다.

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

@SecondaryTable 을 하용하여 한 엔티티에 여러 테이블을 매핑할 수 있지만 이 경우 항상 두 테이블을 조회하므로 최적화하기 어렵다.
따라서 테이블당 엔티티를 각각 만들어서 일대일 매핑하는 것을 권장한다.

profile
iha / ian / inho ha

0개의 댓글