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
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
5. 엔티티 하나에 여러 테이블 매핑하기
- 잘 사용되는 방법은 아니기 때문에 @SecondaryTable을 이용한다는 것만 알아두자.
● 참고자료 : 자바 ORM 표준 JPA 프로그래밍
'Spring 사전 준비 > JPA Hibernate' 카테고리의 다른 글
[JPA] 즉시 로딩과 지연 로딩 (0) | 2022.08.01 |
---|---|
[JPA] 프록시 객체 (0) | 2022.08.01 |
[JPA] 일대일 매핑과 다대다 매핑 (0) | 2022.07.26 |
[JPA] 단방향 매핑과 양방향 매핑 (0) | 2021.08.18 |
[JPA] EntityManager / persistence.xml (0) | 2021.08.16 |
댓글