상속 관계 매핑
위 사진과 같이 id, 이름(name), 가격(price)이라는 동일한 컬럼을 사용하는 엔티티가 여러 개있는 경우 상속 구조를 이용해 공통되는 컬럼을 추출하여 구현할 수 있다.
구현하는 방법으로는 총 3가지가 있으며 부모 클래스까지 직접 테이블과 매핑되는 3가지 방법 외에 마지막으로 부모 클래스는 테이블과 매핑하지 않고 부모 클래스를 상속받는 자식 클래스에게 매핑 정보만 제공하는 @MappedSuperclass 까지 알아보자.
조인 전략 (Joined Strategy)
조인 전략은 엔티티 각각을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략이다. 조회할 때 조인을 자주 사용하며 조인전략을 사용하기 위해서는 타입을 구분하는 컬럼을 추가해야 한다.
장점
- 테이블이 정규화된다.
- 외래 키 참조 무결성 제약조건을 활용할 수 있다.
- 저장공간을 효율적으로 사용한다.
단점
- 조회할 떄 조인이 많이 사용되므로 성능이 저하될 수 있다.
- 조회 쿼리가 복잡하다.
- 데이터를 등록할 INSERT SQL을 두 번 실행한다.
예제 코드
@Entity
@Ingeritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Itam {
@Id @GeneratedValue
@Cloumn(name = "ITEM_ID")
private Long id;
private String name;
private int price;
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
private String artist;
}
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
private String director;
private String actor;
}
@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "BOOK_ID")
public class Book extends Item {
private String author;
private STring isbn;
}
@Inheritance
상속 매핑을 할때 매핑 전략을 지정하는 어노테이션이다. 여기서는 조인 전략을 사용하므로 InheritanceType.JOINED를 사용했다.
@DiscriminatorColumn
부모 클래스에 구분 컬럼을 지정한다. 기본값이 DTYPE이므로 @DiscriminatorColumn으로 줄여서 사용해도 된다.
@DiscriminatorValue
엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정한다.
@PrimaryKeyJoinColumn
자식 테이블의 ID 컬럼명을 변경하고 싶을때 사용한다. 사용하지 않으면 부모 테이블의 ID 컬럼명을 그대로 사용한다.
단일 테이블 전략 (Single-Table Strategy)
단일 테이블 전략은 이름 그대로 테이블을 하나만 사용한다. 조회할 때 조인을 사용하지 않으므로 조회속도가 가장 빠르고 사용하지 않는 컬럼들은 모두 null로 입력되야 하기 때문에 자식 엔티티가 매핑된 컬럼은 모두 null을 허용해야 한다.
장점
- 조인이 필요 없으므로 일반적으로 조회 성능이 빠르다.
- 조회 쿼리가 단순하다.
단점
- 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 한다.
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 그리므로 상황에 따라서는 조회 성능이 오히려 느려질 수 있다.
예제 코드
@Entity
@Ingeritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Itam {
@Id @GeneratedValue
@Cloumn(name = "ITEM_ID")
private Long id;
private String name;
private int price;
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
private String artist;
}
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
private String director;
private String actor;
}
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
private String author;
private STring isbn;
}
구현 클래스마다 테이블 전략 (Table-per-Concrete-Class Strategy)
구현 클래스마다 테이블 전략은 자식 엔티티마다 테이블을 만들고 자식 테이블 각각에 필요한 컬럼이 모두 있다. 일반적으로 추천하지 않는 전략이다.
장점
- 서브 타입을 구분해서 처리할 때 효과적이다.
- not null 제약조건을 사용할 수 있다.
단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느리다.
- 자식 테이블을 통합해서 쿼리하기 어렵다.
예제 코드
구분 컬럼을 사용하지 않기 때문에 @DiscriminatorColumn과 @DiscriminatorValue 어노테이션을 사용하지 않는다.
@Entity
@Ingeritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Itam {
@Id @GeneratedValue
@Cloumn(name = "ITEM_ID")
private Long id;
private String name;
private int price;
}
@Entity
public class Album extends Item {
private String artist;
}
@Entity
public class Movie extends Item {
private String director;
private String actor;
}
@Entity
public class Book extends Item {
private String author;
private STring isbn;
}
@MappedSuperclass
@MappedSuperclass는 부모 클래스는 테이블과 매핑하지 않고 부모 클래스를 상속 받는 자식 클래스에게 매핑 정보만 제공하고 싶을 때 사용한다.
@MappedSuperclass는 추상 클래스와 비슷하다. @Entity는 실제 테이블과 매핑되지만 @MappedSuperclass는 실제 테이블과는 매핑되지 않는다.
@MappedSuperclass
public abstract class BaseEntity {
@Id @GeneratedValue
private Long id;
private String name;
}
@Entity
@AttributeOverride(name = "id", column = @Column(name = "MEMBER_ID"))
public class Member extends BaseEntity {
private String email;
}
@Entity
@AttributeOverrides({
@AttributeOverride(name = "id", column = @Column(name = "SELLER_ID")),
@AttributeOverride(name = "name", column = @Column(name = "SELLER_NAME"))
})
public class Seller extends BaseEntity {
private String shopName;
}
BaseEntity에는 객체들이 주로 사용하는 공통 매핑 정보를 정의해놓고 자식 엔티티들이 상속을 통해 공통 매핑 정보를 상속받아 사용한다.
@AttributeOverrides
여러 매핑 정보를 재정의할 때 사용한다.
@AttributeOverride
매핑 정보를 재정의할 때 사용한다.
@AssociationOverride
여러 연관관계를 재정의할 때 사용한다.
@AssociationOverrides
연관관계를 재정의할 때 사용한다.
사용 예시
@MappedSuperclass를 사용하면 등록일자, 수정일자, 등록자, 수정자 같은 여러 엔티티에서 공통으로 사용하는 속성을 효과적으로 관리할 수 있다.
BaseEntity
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate // 최초 생성 시점
private Timestamp createdAt;
@LastModifiedDate // 마지막 변경 시점
private Timestamp modifiedAt;
}
User
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
@Table(name = "Users")
public class User extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long id;
@Column(nullable = false, unique = true)
private String username; //이메일아이디
@Column(nullable = false)
private String password; //비밀번호
}
'JPA' 카테고리의 다른 글
[JPA] deleteAll() 수행 후 바로 insert 했을 때 duplicate entry 에러가 발생하는 문제 (1) | 2023.04.04 |
---|---|
[JPA] 벌크성 수정 쿼리 (0) | 2023.04.02 |
[Querydsl] 동적쿼리 작성하기 (0) | 2023.01.18 |
[Querydsl] 프로젝션 (0) | 2023.01.17 |
[Querydsl] Querydsl 적용하기 (0) | 2023.01.15 |