이번 시간에는 JPA 값타입에 하나인 임베디드 타입에 대해서 알아보겠습니다.
@Embeddable, @Embedded 어노테이션을 사용해서 구현합니다.
- @Embeddale
// writer.kt
@Embeddable
class Writer(
@Enumerated(EnumType.STRING)
var writerType: UserType,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "writer_seller_id")
var writerSeller: Seller? = null,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "writer_staff_id")
var writerStaff: Staff? = null,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "writer_customer_id")
var writerCustomer: Customer? = null,
)
- @Embedded
JPA 어노테이션 @Embedded는 유형을 다른 엔티티에 임베드하는 데 사용됩니다.
@Embedded 어노테이션을 추가하고 Writer를 사용하도록 했습니다.
그리고 추상클래스로 만들어 사용할 엔티티에 오버라이딩해서 사용할 것입니다.
// Reply.kt
@MappedSuperclass
abstract class Reply(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
open val id: Long,
@Embedded
@AssociationOverrides(
AssociationOverride(name = "writerSeller", joinColumns = [JoinColumn(name = "tag_seller_id")]),
AssociationOverride(name = "writerStaff", joinColumns = [JoinColumn(name = "tag_staff_id")]),
AssociationOverride(name = "writerCustomer", joinColumns = [JoinColumn(name = "tag_customer_id")])
)
open val tagUser: Writer,
)
- 속성 재정의
위에 보시면 @AttributeOverrides속성을 사용해서 Writer포함된 유형의 열 속성을 재정의 한 것을 볼수 있습니다.
- 임베디드 타입과 테이블 매핑
임베디드 타입은 엔티티의 값이다. 그래서 기본타입을 사용했을때와 임베디드 타입을 사용했을때 데이터베이스
테이블의 결과는 동일합니다.
임베디드 타입을 사용함으로 데이터베이스와 객체를 좀 더 세밀하게 매핑가능하며 좀 더 객체지향적인 개발적인 설계를 할 수 있습니다.
- 임베디드 타입이 Null이면 매핑한 컬럼 값은 모두 Null이 된다.
- 값 타입의 실제 인스턴스를 공유하지 말고 Copy해서 사용해야 한다. 그냥 수정 불가하기 하길 바랍니다.(불변객체)
- 값타입을 비교
- 동일성 비교 : 인스턴스의 참조 값을 비교 (==사용)
- 동등성 비교 : 인스턴스의 값을 비교 (equals()사용)
- 값 타입 컬렉션
값 타입을 하나 이상 저장하려면 컬렉션에 보관합니다. @ElementCollection, @CollectionTable 어노테이션 사용합니다.
@Entity
class Shipping(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long,
...
@Embedded
val adddress: Address,
@ElementCollection
@CollectionTable(name="Address", joinColumns = @JoinColumn(name="customer_id"))
val addressHistory: List<Address>
)
// Address.kt
@Embeddable
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
class Address(
@Column
@field:NotNull
@field:NotBlank
@field:Length(min = 4, max = 10)
val zipCode: String?,
@field:NotNull
@field:NotBlank
@field:Length(min = 2, max = 50)
val addressLine: String?,
@field:Length(max = 50)
val addressLine2: String?
)
위의 Shipping 엔티티의 addressHistory는 Address타입을 컬렉션으로 가지고 있습니다.
관계형 데이터베이스에서는 컬럼안에 컬렉션을 포함할 수 없습니다.(Nosql은 가능)
그러므로 별로의 테이블로 존재해야 합니다.
- 값 타입 컬렉션의 제약사항
값 타입은 식별자라는 개념이 없어 값을 변경하면 데이터베이스 추적이 어렵습니다.
값타입은 컬렉션보다는 일대다 관계로 설계하는 것을 추천합니다.
정리
오늘은 @Embbeddable, @Embedded를 사용하여 클래스를 추상클래스안에 포함 시켜 데이터베이스에 매핑하는 것을 알아 보았습니다.
임베디드 타입 사용시 장단점에 대해서도 잘 숙지하시고 사용하시는게 좋을듯 싶습니다.
'IT > JPA' 카테고리의 다른 글
JPA 영속성 컨텍스트를 알아보자 (0) | 2023.04.04 |
---|---|
JPQL – JPA 및 Hibernate에서 쿼리를 정의하는 방법 (0) | 2023.04.03 |
JPA 연관관계 방향성 (이론편) (0) | 2023.03.31 |
JPA Entity 애노테이션 알아보기 (0) | 2022.11.09 |
JPA Entity를 만들어 보자 (0) | 2022.11.09 |