들어가며
7강은 1편과 2편으로 나눠 진행하였다.
7.1 상속 관계 매핑
- 관계형 디비에는 객체지향 언어에서 다루는 상속 개념이 없다
- 대신 슈퍼타입 서브타입 관계라는 모델링 기법이 객체의 상속 개념과 가장 유사
- ORM에서 이야기하는 상속 관계 매핑은 객체의 상속 구조와 디비의 슈퍼타입 서브타입 관계를 매핑하는 것
- 슈퍼타입 서브타입 논리 모델을 실제 물리 모델인 테이블로 구현 시 3가지 방법 존재
- 각각의 테이블로 변환 : 각각을 모두 테이블로 만들고 조회할 때 조인을 사용 (JPA에서는 조인 전략이라 함)
- 통합 테이블로 변환 : 테이블을 하나만 사용해서 통합 (JPA에서는 단일 테이블 전략이라 함)
- 서브타입 테이블로 변환 : 서브 타입마다 하나의 테이블을 만든다 (JPA에서는 구현 클래스마다 테이블 전략이라 함)
7.1.1 조인 전략
- 조인 전략은 엔티티 각각을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략이다.
- 따라서 조회할 때 조인을 자주 사용
- 주의점은 객체는 타입으로 구분할 수 있지만 테이블은 타입의 개념이 없으므로 타입을 구분하는 컬럼을 추가해야함
- DTYPE 컬럼을 구분 컬럼으로 추가하여 사용
- 매핑 정보 분석
- @Inheritance(starategy = InheritanceType.JOINED) : 상속 매핑은 부모 클래스에 @Inheritance 를 사용하며 조인 전략을 사용하므로 JOINED 사용
- @DiscriminatorColumn(name = “DTYPE”) : 부모 클래스에 구분 컬럼을 지정하고 이 컬럼으로 저장된 자식 테이블을 구분 가능, DTYPE이 기본값이다.
- @DiscriminatorValue(“M”) : 엔티티를 저장할 때 구분 컬럼에 입력할 값 지정, 해당 어노테이션이 적용된 엔티티 저장 시 구분 컬럼이 DTYPE에 값 M이 저장
- 기본 값으로 자식 테이블은 부모 테이블의 ID 컬럼명을 그대로 사용하는데 만약 자식 테이블의 기본 키 값 변경하고 싶을땐 @PrimaryKeyJoinColumn을 사용
- 조인 전략의 장단점
- 장점
- 테이블이 정규화된다
- 외래 키 참조 무결성 제약조건을 활용할 수 있다.
- 저장공간을 효율적으로 사용한다
- 단점
- 조회할 때 조인이 많이 사용되므로 성능 저하
- 조회 쿼리 복잡
- 데이터를 등록할 INSERT SQL을 두 번 실행 -> 두번이라는게?
- 특징
- JPA 표준 명세는 구분 컬럼을 사용하도록 하지만 하이버네이트를 포함한 몇 몇 구현체는 구분 컬럼 (@DiscriminatorColumn) 없이도 동작함
- 장점
7.1.2 단일 테이블 전략
- 테이블을 하나만 사용
- 구분 컬럼(DTYPE)으로 어떤 자식 데이터가 저장되었는지 구분
- 조회할 때 조인을 사용하지 않으므로 일반적으로 가장 빠름
- 해당 전략 사용 시 주의점은 자식 엔티티가 매핑한 컬럼을 모두 null을 허용해야 함
- @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 로 지정하면 단일 테이블 전략 사용
- 테이블 하나에 모든 것을 통합하므로 구분 컬럼을 필수로 사용해야함
- 장점
- 조인이 필요 없으므로 일반적으로 조회 성능이 빠르다
- 조회 쿼리가 단순하다
- 단점
- 자식 엔티티가 매핑한 컬럼을 모두 null을 허용해야 한다
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있어 상황에 따라서는 조회 성능이 오히려 느려질 수 있다.
- 특징
- 구분 컬럼을 꼭 사용해야한다.
- 따라서 @DiscriminatorColumn을 꼭 설정해줘야 함
- @DiscriminatorValue를 지정하지 않으면 기본적으로 엔티티 이름을 사용
7.1.3 구현 클래스마다 테이블 전략
- 자식 엔티티마다 테이블을 만듦
- 자식 테이블 각각에 필요한 컬럼이 모두 있음
- @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 를 사용하면 구현 클래스마다 테이블 전략을 사용한다.
- 일반적으로 추천하지 않는 전략
- 장점
- 서브 타입을 구분해서 처리할 때 효과적
- not null 제약조건을 사용할 수 있음
- 단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느리다 (SQL UNION을 사용해야 함)
- 자식 테이블을 통합해서 쿼리하기 어려움
- 특징
- 구분 컬럼을 사용하지 않음
7.2 @MappedSuperclass
- 7.1에서 알아본 내용인 상속 관계 매핑은 부모 클래스와 자식 클래스를 모두 디비 테이블과 매핑한 것
- 부모클래스는 테이블과 매핑하지 않고 부모 클래스를 상속 받는 자식 클래스에게 매핑 정보만 제공하고 싶으면 @MappedSuperclass를 사용한다
- 서로 관계가 없는 멤버와 판매자 엔티티가 있을 때 두 객체 모델의 공통 속성인 id, name을 부모 클래스로 모으고 객체 상속 관계를 만들고 해당 부모 클래스인 BaseEntity는 테이블과 매핑할 필요가 없고 자식 엔티티에게 공통으로 사용되는 매핑 정보만 제공하면 되므로 이럴때 @MappedSuperclass를 사용
- 이때 부모로부터 물려받은 매핑 정보를 재정의하려면 @AttributeOverrides(둘 이상 재정의 시)나 @AttributeOverride를 사용한다.
- 연관관계를 재정의하려면 @AssociationOverrides나 @AssociationOverride를 사용
- @MappedSuperclass 특징
- 테이블과 매핑되지 않고 자식 클래스에 엔티티의 매핑 정보를 상속하기 위해 사용
- @MappedSuperclass로 지정한 클래스는 엔티티가 아니므로 em.find()나 JPQL에서 사용 불가능
- 이 클래스를 직접 생성해서 사용할 일은 거의 없으므로 추상 클래스로 만드는 것을 권장
- 즉 @MappedSuperclass는 테이블과는 관계가 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모아주는 역할을 할 뿐
- @MappedSuperclass를 사용하면 등록일자, 수정일자, 등록자, 수정자 같은 여러 엔티티에서 공통으로 사용하는 속성을 효과적으로 관리 가능
- 참고 : 엔티티는 엔티티이거나 @MappedSuperclass로 지정한 클래스만 상속받을 수 있음
7.3 복합 키와 식별 관계 매핑
7.3.1 식별관계 vs 비식별 관계
- 디비 테이블 사이에 관계는 외래 키가 기본 키에 포함되는지 여부에 따라 식별 관계와 비식별 관계로 구분
- 식별 관계
- 식별 관계는 부모 테이블의 기본 키를 내려받아서 자식 테이블의 기본 키 + 외래키로 사용하는 관계
- 비식별 관계
- 비식별 관계는 부모 테이블의 기본 키를 받아서 자식 테이블의 외래 키로만 사용하는 관계
- 외래 키에 NULL 허용 여부에 따라 필수적 비식별 관계와 선택적 비식별 관계로 나눈다.
- 필수적 비식별 관계 : 외래 키에 NULL을 허용하지 않고, 연관관계를 필수적으로 맺어야 함
- 선택적 비식별 관계 : 외래 키에 NULL을 허용하고, 연관관계를 맺을지 말지 선택 가능
7.3.2 복합 키 : 비식별 관계 매핑
- JPA에서 식별자를 둘 이상 사용하려면 별도의 식별자 클래스를 만들어야 함
- JPA는 영속성 컨텍스트에 엔티티를 보관할 때 엔티티의 식별자를 키로 사용
- 식별자를 구분하기 위해 equals와 hashCode를 사용해서 동등성 비교를 한다
- 식별자가 하나일때는 보통 자바의 기본 타입을 사용하므로 문제가 없지만 식별자 필드가 2개 이상이면 별도의 식별자 클래스를 만들고 그곳에 equals와 hashCode를 구현해야 한다
- JPA는 복합 키를 지원하기 위해 @IdClass와 @EmbeddedId 두가지 방법 제공
- @IdClass
- 관계형 데이터베이스에 가까운 방법
- PARENT 테이블에 있는 기본키 두개를 CHILD 테이블이 내려받았을 때 복합 키를 매핑하기 위해 식별자 클래스를 별도로 만들어야 함
- 각각의 기본 키 컬럼을 @Id로 매핑 후 @IdClass를 사용하여 ParentId 클래스를 식별자 클래스로 지정
- @IdClass를 사용 시 식별자 클래스는 다음 조건 만족하여야 함
- 식별자 클래스의 속성명과 엔티티에서 사용하는 식별자의 속성명이 같아야함
- Serializable 인터페이스를 구현해야 함
- equals, hashCode 구현
- 기본 생성자 존재
- 식별자 클래스는 public
- 부모 테이블의 기본 키 컬럼이 복합 키면 자식 테이블의 외래 키도 복합 키므로 외래키 매핑 시 여러 컬럼을 매핑해야 하므로 @JoinColumns 어노테이션을 사용하고 각각을 @JoinColumn으로 매핑한다.
- @EmbeddedId
- 객체지향에 가까운 방법
- Parent 엔티티에서 식별자 클래스를 직접 사용하고 @EmbeddedId 어노테이션을 적어주면 됨
- @IdClass와는 다르게 @EmbeddedId를 적용한 식별자 클래스는 식별자 클래스에 기본 키를 직접 매핑한다.
- @EmbeddedId를 적용한 식별자 클래스는 다음 조건을 만족
- @Embeddable 어노테이션을 붙여줘야 함
- Serializable 인터페이스 구현
- equals, hashCode 구현
- 기본 생성자 존재
- 식별자 클래스 public
- 복합 키와 equals(), hashCode()
- 복합 키는 해당 두 메서드를 필수로 구현해야함
- 기본 제공하는 equals()는 인스턴스 참조 값 비교를 하기 때문에 상황에 따라 적절히 오버라이딩 필요
- 따라서 식별자 객체의 동등성이 지켜지지 않으면 예상과 다른 엔티티가 조회되거나 엔티티를 찾을 수 없는 등 영속성 컨텍스트가 엔티티를 관리하는 데 문제가 발생할 수 있음
- @IdClass vs @EmbeddedId
- 각각 장단점이 있으므로 취향에 맞게 사용
- @EmbeddedId가 @IdClass와 비교해서 더 객체지향적이고 중복도 없어 좋아보이지만 특정 상황에 JPQL이 다음과 같이 길어질 수 있음
7.3.3 복합 키 : 식별 관계 매핑
- @IdClass와 식별 관계
- 식별 관계는 기본 키와 외래 키를 같이 매핑
- 따라서 식별자 매핑인 @Id와 연관관계 매핑인 @ManyToOne과 @JoinColumn으로 외래 키를 같이 매핑
- @EmbeddedId와 식별 관계
- @EmbeddedId로 식별 관계를 구성할 때 @MapsId를 사용해야 함
- @MapsId는 외래 키와 매핑한 연관관계를 기본 키에도 매핑하겠다는 뜻
7.3.4 비식별 관계로 구현
- 식별 관계의 복합 키를 사용한 코드와 비교 시 매핑도 쉽고 코드도 단순
- 복합 키가 없으므로 복합 키 클래스를 만들지 않아도 됨
7.3.5 일대일 식별 관계
- 자식 테이블의 기본 키 값으로 부모 테이블의 기본 키 값만 사용
- 부모 테이블의 기본 키가 복합 키가 아니면 자식 테이블의 기본 키는 복합 키로 구성하지 않아도 됨
7.3.6 식별, 비식별 관계의 장단점
- 디비 설계 관점에서 보면 다음과 같은 이유로 식별 관계보다는 비식별 관계를 선호
- 식별 관계는 부모 테이블의 기본 키를 자식 테이블로 전파하면서 자식 테이블의 기본 키 컬럼이 점점 늘어남 -> 부모 테이블 기본키 1개 / 자식 테이블은 기본키 컬럼 2개 / 손자 테이블은 기본키 3개로 점점 늘어나 조인시 SQL이 복잡해지고 기본 키 인덱스가 불필요하게 커짐
- 식별 관계는 2개 이상의 컬럼을 합해서 복합 기본 키를 만들어야 하는 경우가 많음
- 식별 관계를 사용할 때 기본 키로 비즈니스 의미가 있는 자연 키 컬럼을 조합하는 경우가 많음 -> 반면 비식별 관계의 기본 키는 비즈니스와 전혀 관계없는 대리 키를 주로 사용함. 즉, 비즈니스 요구사항은 시간이 지남에 따라 언젠가는 변하기 때문에 비식별관계 권장
- 식별 관계는 부모 테이블의 기본 키를 자식 테이블의 기본 키로 사용하므로 비식별 관계보다 테이블 구조가 유연하지 못함
- 객체 관계 매핑의 관점에서의 비식별 관계 선호 이유
- 일대일 관계를 제외하고 식별 관계는 2개 이상의 컬럼을 묶은 복합 기본 키를 사용하므로 JPA에서 복합 키는 별도의 복합 키 클래스를 만들어서 사용해야 하는데 컬럼이 하나인 기본 키를 매팡하는 것보다 많은 노력이 필요하게 됨
- 비식별 관계의 기본 키는 주로 대리 키를 사용하는데 JPA는 @GenerateValue처럼 대리 키를 생성하기 위한 편리한 방법을 제공해줌
- 식별 관계가 가지는 장점은?
- 기본 키 인덱스를 활용하기 좋고, 상위 테이블들의 기본 키 컬럼을 자식, 손자 테이블들이 가지고 있어 특정 상황에 조인 없이 하위 테이블로만 검색을 완료 가능
- ORM 신규 프로젝트 진행 시 추전 방법
- 비식별 관계를 사용하고 기본 키는 Long 타입의 대리 키를 사용하는 것
- 유연한 대처가 가능하다는 장점
- Integer는 20억 정도면 끝나버리므로 많은 양의 데이터 저장 시 문제 생길 수 있지만 Long 타입은 약 920경 이라 안전
- 그리고 선택적 비식별 관계보다는 필수적 비식별 관계를 사용하는 것이 좋음
- 이유는 선택적인 비식별 관계는 NULL을 허용하므로 조인할 때에 외부 조인을 사용해야 하는데 필수적 관계는 NOT NULL로 항상 관계가 있다는 것을 보장하므로 내부 조인만 사용해도 되기 때문
'Book' 카테고리의 다른 글
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (8-1) 프록시와 연관관계 정리 (0) | 2022.08.27 |
---|---|
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (7-2) 고급매핑 (0) | 2022.08.24 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (6) 다양한 연관관계 매핑 (0) | 2022.08.12 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (5-2) 연관관계 매핑 기초 (0) | 2022.08.07 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (5-1) 연관관계 매핑 기초 (0) | 2022.08.01 |
댓글