-
JPA - Embedded Type🌱 spring 2022. 11. 2. 17:23
🗂 JPA의 데이터 타입 분류
엔티티 타입
- @Entity로 정의하는 객체
- 데이터가 변해도 식별자로 지속해서 추적 가능
값 타입
- Integer, String 처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체를 의미
- 식별자가 없고 값만 존재 → 변경시 추적 불가
값 타입 분류
- 기본 값 타입
- 자바 기본 타입 (int, double)
- 래퍼 클래스(Integer, Long)
- String
- 임베디드 타입
- 컬렉션 값 타입
임베디드 타입(Embedded Type) ❓
복합 값 타입을 의미한다
- 새로운 값 타입을 직접 정의할 수 있다.
- JPA는 임베디드 타입 이라고 불려진다
- 주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고 한다.
직접 정의한 임베디드 타입도 int, String 처럼 값 타입이다!!! ⭐
사용방법
- @Embeddeable
- 값 타입을 정의하는 곳에 표시
- 임베디드 타입을 사용하기 위해 생성한 Class에 표시
- @Embedded
- 값 타입을 사용하는 곳에 표시
- 임베디드 타입을 사용하는 Entity에 표시
임베디드 타입은 기본 생성자가 필수다!!
임베디드 타입을 포함한 모든 값 타입은 엔티티의 생명주기에 의존한다
→ 엔티티와 임베디드 타입 관계는 컴포지션(Composition) 관계가 된다.
예시
사용하기 전
// 임베디드 타입 사용하지 않았을 때 @Entity public class Member { @Id @GeneratedValue private Long id; private String name; // 근무 기간 @Temporal(TemporalType.DATE) Date startDate; @Temporal(TemporalType.DATE) Date endDate; // 집 주소 표현 private String city; private String street; private String zipcode; // ... }
- 평범한 회원 엔티티
회원 엔티티는 [ 이름, 근무 시작일, 근무 종료일, 주소 도시, 주소 번지, 주소 우편 번호 ] 를 가진다
위 보다 아래의 설명이 더 정확하다.
회원 엔티티는 [ 이름, 근무 기간, 집 주소 ] 를 가진다.
회원이 상세한 데이터를 그대로 가지고 있는 것은 객체지향적 ❌, 응집력만 떨어뜨린다.
사용한 후
Member.java
// 임베디드 타입 사용 @Entity public class Member { @Id @GeneratedVAlue private Long id; private String name; @Embedded private Period workPeriod; // 근무 기간 @Embedded private Address homeAddress; // 집 주소 }
Period.jave
// 기간 임베디드 타입 @Embeddable public class Peroid { @Temporal(TemporalType.DATE) Date startDate; @Temporal(TemporalType/Date) Date endDate; // ... public boolean isWork (Date date) { // .. 값 타입을 위한 메서드를 정의할 수 있다 } }
Address.java
@Embeddable public class Address { @Column(name="city") // 매핑할 컬럼 정의 가능 private String city; private String street; private String zipcode; // ... }
임베디드 타입이 null이라면 매핑한 컬럼 값은 모두 null이 된다.
member.setAddress(null); // null 입력 em.persist(member);
회원 테이블의 주소와 관련된 모든 컬럼이 null이 된다.
장점
- 재사용이 가능하다
- 높은 응집도를 가진다
- 값 타입만 사용하는 의미 있는 메서드를 만들 수 있다.
- ex) Period 객체의 isWork() 메서드
임베디드 타입과 테이블 매핑
임베디드 타입은 엔티티의 값일 뿐이다.
- 따라서 값이 속한 엔티티의 테이블에 매핑한다.
⇒ 엔티티 안에서 임베디드 타입을 사용해 클래스를 나누는 것은 데이터베이스 테이블의 컬럼과 전혀 관계가 없다.
⇒ 임베디드 타입을 사용하기 전, 후의 테이블 차이는 없다.
따라서 잘 설계된 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많다.
임베디드 타입과 연관관계
임베디드 타입은 값 타입을 포함하거나 엔티티를 참조할 수 있다.
@Embeddable public class Address { String street; String city; String state; @Embedded Zipcode zipcode; // 임베디드 타입 포함 } @Embeddable public class Zipcode { String zip; String plusFour; } @Embeddable public class PhoneNumber { String areaCode; String localNumber; @ManyToOne PhoneServiceProvider provider; // 엔티티 참조 } @Entity public class PhoneServiceProvider { @Id String name; // ... }
- 값 타입인 Address 가 값 타입인 Zipcode를 포함
- 값 타입인 PhoneNumber 가 엔티티 타입인 PhoneServiceProvider를 참조
@AttributeOverride - 속성 재정의
임베디드 타입에 정의한 매핑정보를 재정의하려면 엔티티에 @AttributeOverride 를 사용
회원에 주소가 하나 더 필요한 상황
// 같은 임베디드 타입을 가지고 있는 회원 @Entity public class Member { @Id @GeneratedValue private Long id; private String name; @Embedded Address homeAddress; @Embedded Address companyAddress; }
- 문제점 : 테이블에 매핑하는 컬럼명이 중복된다.
- @AttributeOverrides를 사용해야 한다.
@Entity public class Member { @Id @GeneratedValue private Long id; private String name; @Embedded Address homeAddress; @Embedded @AttributeOverrides({ @AttributeOverride(name="city", column=@Column(name="COMPANY_CITY")), @AttributeOverride(name="street", column=@Column(name="COMPANY_STREET")), @AttributeOverride(name="zipcode", column=@Column(name="COMPANY_ZIPCODE")) }) Address companyAddress; }
- 매핑 정보를 재정의
생성된 테이블
CREATE TABLE MEMBER ( COMPANY_CITY varchar(255), COMPANY_STREET varchar(255), COMPANY_ZIPCODE varchar(255), city varchar(255), street varchar(255), zipcode varchar(255), ... )
@AttributeOverrides 는 엔티티에 설정을 해야한다.
임베디드 타입이 임베디드 타입을 가지고 있더라도 엔티티에 설정을 해야 한다.
참고 자료
https://tall-developer.tistory.com/m/16
https://velog.io/@conatuseus/JPA-임베디드-타입embedded-type-8ak3ygq8wo
'🌱 spring' 카테고리의 다른 글
애플리케이션 컨텍스트와 빈팩토리 (0) 2022.11.16 JPA - @ElementCollection (0) 2022.11.02 Spring Scheduler (0) 2022.10.24 Filter, Interceptor (0) 2022.10.11 OSIV 🧐 (0) 2022.10.11