오늘은 JPA 어노테이션인 @Embedded @Embeddable 에 대해서 알아보겠습니다.
서론
JPA 는 Java 에서 사용할 수 있는 ORM 으로 정말 유용한 도구 입니다.
보통 Java,Spring 개발자들은 Spring Data JPA 를 사용합니다.
Spring Data JPA 는 추상화가 아주 잘되어 있어 사용에 정말 유용 합니다.
하지만 사용에 유용한 만큼, 잘 알고 사용할 필요가 있습니다.
Spring Data JPA 를 추가하게 되면
jakarta.persistence;
위 패키지에서 추상화가 잘된 인터페이스 및 어노테이션을 사용할 수 있습니다.
그리고 공식문서 또한 보기 좋게 되어 있어, 개발 생산성에 아주 좋다고 생각합니다.
왜 JPA Entity 에서 @Embeddalbe , @Embedded 를 사용한다고 생각하시나요?
저는 위 어노테이션을 사용했을 때 객체지향 에 특성을 더 잘 살릴 수 있기 때문이라고 생각합니다.
📌 장점
1) 코드의 재사용
2) 가독성 향상
3) 높은 응집도
한 번 예시를 보며 한 번에 이해 할 수 있도록 도와드리겠습니다.
본론
아래 코드는 @Embeddable 을 설명하는 공식 문서입니다.
/**
* Specifies a class whose instances are stored as an intrinsic
* part of an owning entity and share the identity of the entity.
* Each of the persistent properties or fields of the embedded
* object is mapped to the database table for the entity.
*
* <p> Note that the {@link Transient} annotation may be used to
* designate the non-persistent state of an embeddable class.
*
* <pre>
*
* Example 1:
*
* @Embeddable
public class EmploymentPeriod {
* @Temporal(DATE) java.util.Date startDate;
* @Temporal(DATE) java.util.Date endDate;
* ...
* }
*
* Example 2:
*
* @Embeddable
public class PhoneNumber {
* protected String areaCode;
* protected String localNumber;
* @ManyToOne PhoneServiceProvider provider;
* ...
* }
*
* @Entity
public class PhoneServiceProvider {
* @Id
protected String name;
* ...
* }
*
* Example 3:
*
* @Embeddable
public class Address {
* protected String street;
* protected String city;
* protected String state;
* @Embedded
protected Zipcode zipcode;
* }
*
* @Embeddable
public class Zipcode {
* protected String zip;
* protected String plusFour;
* }
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface Embeddable {
}
위 코드에서 들어주시는 예시를 보시면 감이 좀 잡히시나요?
저는 영어를 잘하지 못해서 번역기를 돌리면서 읽습니다ㅎㅎ
위 코드를 보시면 이해가 잘 되시나요? 이해를 돕기 위해서 위 Example3 를 예시로 설명 드리겠습니다.
일단 첫번 째로 위 어노테이션을 뜻을 검색해보시고 뜻을 알면 이해하기 쉽습니다.
Embedded - 내장되어있는
Embeddable - 내장 가능한
이 정도로 해석할 수 있습니다.
위 뜻을 보시면 짐작이 가시나요? 한번 코드를 봐보시죠
// 이해를 돕기 위해 클래스 한개 추가함
public class Order {
@Embedded
private Address address;
}
@Embeddable
public class Address {
protected String street;
protected String city;
protected String state;
@Embedded protected Zipcode zipcode;
}
@Embeddable
public class Zipcode {
protected String zip;
protected String plusFour;
}
그냥 임의로 Order 클래스를 추가 했습니다. Order 인 이유는 Address 를 필요로 할 만한 내용이 대표적으로 주문 이 있기 때문 입니다.
그러면 구조가 최상위 Order - Address - Zipcode 순으로 되어 있습니다.
1) Order 안에 Address 가 내장되어 있음
2) Address 안에 Zipcode 가 내장되어 있음.
구조는 위 처럼 되어 있습니다.
즉 루트 객체에 포함되어 있는 하위 객체이거나 내가 어딘가에 포함이 될 수 있다고 생각하는 객체에 @Embeddable 을 사용합니다.
즉 Address 는 따로 테이블로 존재하지 않고, Order 에 속해있든, Member(그냥 예시임) 에 속해있든 어딘가에 속하는 객체이기 때문에
@Embeddable 을 사용해서 가독성을 올려줍니다.
@Embeddable 어노테이션은 간단하게 말하면 나는 메인 객체가 아니고, 어딘가에 포함되는 그냥 서브 객체이다?
정도 느낌으로 알고있으면 좋을거 같습니다.
즉, 나는 다른 엔티티에 의해 embedded 될 것이라고 선언하는 것 입니다.
그럼 이제 @Embeddable 을 알았으니, 위 어노테이션이 선언된 객체 상위 객체에서 사용할 때는 명시적으로
@Embeddable 객체를 사용한다는 걸 표시하기 위해 @Embedded 를 사용합니다.
즉, @Embedded 는 @Embeddable 이 선언된 객체를 다른 엔티티에 포함시키는 데 사용됩니다.
@Embedded 문서를 보시죠
위 @Embedded 문서를 보면 뭔가 제가 설명한 것과 다르게 이상한게 많지 않나요?
위 Example 에는 처음보는 @AttributeOverride(s), AssociationOverride(s) 등 해석하기 까다로워 보이는 어노테이션들이 있습니다.
간단한게 설명을 하면 위 클래스는 Entity 클래스에 포함되는 또 다른 클래스이며,
하위 클래스에서 임베디드 클래스의 특정 필드에 대한 매핑을 재정의하는 어노테이션 입니다.
✅ AttributeOverride:
@AttributeOverrides -> 임베디드 클래스 여러 매핑 재정의
@AttiributeOverride -> 임베디드 클래스 1개 매핑 재정의
✅ AssociationOverride:
@AssociationOverrides -> 임베디드 클래스 여러 개의 연관 관계 필드의 매핑을 재정의
@AssociationOverride -> 임베디드 클래스의 연관 관계 필드에 대한 1개 매핑을 재정의..
🖐🏻 간단하게 정리를 하면
- @AttributeOverride와 @AssociationOverride는 임베디드 클래스의 필드 매핑을 재정의할 때 사용합니다.
- 관련된 여러 매핑 재정의는 @AttributeOverrides와 @AssociationOverrides를 사용해 그룹화할 수 있습니다.
추가적으로 @Entity 와 @Embeddable 로 클래스를 매핑하려면 기본 생성자를 제공해야 합니다.
DB에서 데이터를 읽어와 매핑된 객체를 생성할 때 기본 생성자를 사용해서 객체를 생성할 때 기본 생성자를 사용해서
객체를 생성하기 때문이다.
결론
🖐🏻 간단 정리
@Embeddable : 임베디드 클래스를 정의하는 곳에 선언
@Embedded : 임베디드 클래스를 엔티티에 포함시킬 때 선언
REF: https://www.baeldung.com/jpa-embedded-embeddable