- 서브타입 테이블로 구현 (구현 클래스마다 테이블을 만드는 전략) - @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 각각의 클래스마다 별도의 데이터베이스 테이블을 생성하여 클래스에 정의된 속성을 저장하는 방법입니다. 각 클래스에 대한 테이블은 해당 클래스의 속성을 직접 매핑하여 저장하며, 부모 클래스와 자식 클래스 간의 관계를 데이터베이스 수준에서 처리합니다.
장점
- 클래스별 정규화: 각 클래스에 대한 별도의 테이블을 사용하므로 데이터를 정규화하여 중복을 줄일 수 있습니다. 클래스에 특화된 속성만 저장하여 데이터 일관성을 유지하기 쉽습니다.
- 각 테이블에 특화된 제약 조건: 각 테이블에 클래스에 특화된 제약 조건을 선언적으로 추가할 수 있습니다. 이로써 데이터 무결성과 검증이 강화됩니다.
- 선언적인 관계: 관계를 선언적으로 정의하여 객체 간의 연결성을 유지할 수 있습니다.
단점
- 복잡성: 각 클래스마다 테이블이 생성되므로 데이터베이스 스키마가 복잡해질 수 있습니다. 쿼리 시 조인을 사용해야 하며, 복잡한 쿼리 작성이 필요할 수 있습니다.
- 조회 성능: 데이터가 여러 테이블에 분산되어 있기 때문에 쿼리 시 조인을 사용해야 하며, 이로 인해 성능이 저하될 수 있습니다.
- 변경 관리: 클래스의 변경이 발생하면 해당 클래스에 대응하는 테이블도 변경해야 합니다. 클래스 구조의 변경이 데이터베이스 스키마 변경으로 이어지므로 유지 관리에 주의가 필요합니다.
- 비효율적인 공간 사용: 테이블 간 중복 데이터가 발생할 수 있고, 인덱스 및 제약 조건도 중복될 수 있어 공간 사용이 비효율적일 수 있습니다.
2. 조인 테이블로 구현 (조인 전략) - @Inheritance(strategy = InheritanceType.JOINED) 각각의 클래스에 대해 별도의 데이터베이스 테이블을 생성하되, 부모 클래스와 자식 클래스 간의 관계를 조인을 통해 처리하는 방법을 의미합니다. 이로써 부모 클래스와 각 하위 클래스 간의 데이터 일관성을 유지하면서도 각 클래스에 특화된 속성을 저장할 수 있습니다.
장점
- 정규화와 일관성: 각 클래스에 특화된 테이블을 사용하므로 데이터를 정규화하여 중복을 줄일 수 있습니다. 데이터 일관성을 유지하면서 각 클래스의 특성을 저장할 수 있습니다.
- 유연성: 새로운 하위 클래스를 추가하거나 기존 클래스를 변경해도 데이터베이스 스키마를 변경하지 않아도 됩니다. 클래스 구조의 변경이 데이터베이스 스키마 변경으로 이어지지 않습니다.
- 쿼리 성능: 각 클래스의 특화된 테이블을 사용하기 때문에 필요한 속성만 조회하면 됩니다. 조인이 필요하지 않으므로 쿼리 성능이 향상될 수 있습니다.
- 클래스 간 분리: 각 클래스에 해당하는 테이블을 별도로 유지하므로 클래스 간의 결합도가 낮아집니다. 각 클래스를 독립적으로 관리하고 확장할 수 있습니다.
단점
- 복잡한 쿼리: 필요한 속성을 조회하려면 조인을 사용해야 하므로 쿼리가 복잡해질 수 있습니다. 특히 클래스 계층이 복잡한 경우에는 조인이 많이 발생할 수 있습니다.
- 조인의 영향: 조인은 데이터베이스 성능에 영향을 미칠 수 있습니다. 조인된 테이블의 크기와 인덱스 사용 등을 고려하여 성능을 최적화해야 합니다.
- 데이터 일관성 유지: 여러 테이블에 데이터가 분산되어 있기 때문에 데이터 일관성을 유지하는 데 주의가 필요합니다. 부모 클래스와 하위 클래스 간의 관계를 올바로 설정해야 합니다.
- 스키마 복잡성: 각 클래스에 특화된 테이블을 유지하면서도 클래스 간의 관계를 조인으로 처리하기 때문에 스키마가 복잡해질 수 있습니다.
Join 전략에서는 @DiscriminatorColumn어노테이션을 사용하는데, 상속 계층이 관계형 데이터베이스에 저장되는 방법의 세부 정보를 지정하는 데 사용됩니다. 일반적으로 단일 테이블 상속, 조인 테이블 상속 또는 클래스별 테이블 상속과 같은 상속 전략과 함께 사용됩니다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype", discriminatorType = DiscriminatorType.STRING)
public class Vehicle {
// 모든 차량에 공통적인 속성 및 메서드
}
@Entity
@DiscriminatorValue("CAR")
public class Car extends Vehicle {
// 자동차에만 해당하는 속성 및 메서드
}
@Entity
@DiscriminatorValue("BIKE")
public class Bike extends Vehicle {
// 자전거에만 해당하는 속성 및 메서드
}
이 예시에서 Vehicle 클래스는 두 개의 하위 클래스 Car와 Bike를 가진 부모 클래스입니다. @DiscriminatorColumn 주석은 디스크리미네이터 컬럼의 이름이 "dtype"이고 데이터 유형이 문자열임을 지정합니다. 하위 클래스는 @DiscriminatorValue 주석을 사용하여 디스크리미네이터 컬럼에서 해당 유형을 나타내는 값을 지정합니다.
@DiscriminatorColumn이 작동하는 방식은 다음과 같습니다.
상속 전략 : 관계형 데이터베이스에 매핑하려는 클래스의 계층 구조(부모 및 자식 클래스)가 있는 경우 이를 어떻게 저장할지 결정해야 합니다. JPA는 이를 위한 다양한 전략을 제공하며, 그 중 하나가 디스크리미네이터 기반 전략입니다.
DiscriminatorColumn : 데이터베이스 테이블에서 각 행에 저장된 객체 유형에 대한 정보를 보유하는 열입니다. 이는 어떤 하위 클래스가 해당 행에 해당하는지를 나타냅니다. 이를 통해 JPA 공급자는 데이터베이스를 쿼리할 때 올바른 유형의 객체를 검색할 수 있습니다.
@DiscriminatorColumn : 디스크리미네이터 컬럼을 구성하려면 @DiscriminatorColumn 을 사용합니다. 이 주석은 일반적으로 계층 구조의 부모 클래스에 배치됩니다. 이를 사용하여 디스크리미네이터 컬럼의 이름, 데이터 유형 및 필요한 경우 각 하위 클래스에 해당하는 값을 지정할 수 있습니다.
DB입장에서는 어떤 전략을 사용하여 구현을 하더라도 JPA는 매핑이 가능하도록 지원을 해줍니다. 일반적으로 많은 서브타입이 존재하고, 모든 서브타입 데이터에 접근해야 하는 경우 조인 전략을 선택합니다. 서브타입 데이터에 대한 접근이 빠른 것이 중요한 경우 단일 테이블 전략을 선택합니다. 테이블이 작고 효율적인 것이 중요한 경우 테이블 당 클래스 전략을 선택합니다.
각 방법은 장단점을 가지고 있으며, 데이터베이스의 크기와 복잡성, 애플리케이션의 요구사항에 따라 선택되어야 합니다. 가장 적합한 방법을 결정할 때는 데이터베이스 설계 원칙과 성능 최적화를 함께 고려해야 합니다.
'[ BACKEND] > Spring' 카테고리의 다른 글
[SPRING] 즉시 로딩과 지연 로딩 (0) | 2023.08.18 |
---|---|
[SPRING] ✅ Proxy (0) | 2023.08.17 |
[SPRING] 연관관계 고급 매핑 (0) | 2023.08.15 |
[SPRING] 객체 참조 및 연관 관계 ✍️ (0) | 2023.08.13 |
[SPRING] 다양한 연관관계의 종류 (0) | 2023.08.11 |