JPA는 데이터 타입을 크게 두 가지로 분류합니다.
JPA 에서 새로운 값 타입을 직접 정의해서 사용할 수 있습니다.
회원(Member) 도메인에 근무기간(Work Period) 과 집 주소(Home Address)의 속성을 추가해 보도록 하겠습니다.
Member Entity 코드
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
// 근무 기간
@Temporal(TemporalType.DATE)
Date startDate;
@Temporal(TemporalType.DATE)
Date endDate;
// 집 주소 표현
private String city;
private String street;
private String zipcode;
}
즉, 회원 엔티티는 이름, 근무 시작일, 근무 종료일, 집 주소 도시, 집 주소 번지, 집 주소 우편 번호 의 속성을 갖습니다.
아래와 같이 표현하면 더 명확하게 표현할 수 있습니다.
회원 엔티티는 이름, 근무 기간, 집 주소를 갖습니다.
Member Entity 코드
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@Embedded Period workPeriod; // 근무 기간
@Embedded Address homeAddress; // 집 주소
}
기간 임베디드 타입
@Embeddable
public class Period {
@Temporal(TemporalType.DATE)
Date startDate;
@Temporal(TemporalType.DATE)
Date endDate;
// ..
public boolean isWork(Date date) {
// .. 값 타입을 위한 메소드를 정의할 수 있습니다.
}
}
주소 임베디드 타입
@Embeddable
public class Address {
@Column (name="city") // 매핑할 컬럼 정의 가능
private String city;
private String street;
private String zipcode;
}
임베디드 타입 사용을 위해서, 다음 2가지 어노테이션이 필요합니다.
임베디드 타입을 포함한 모든 값 타입은 엔티티의 생명주기에 의존하므로, 엔티티와 임베디드 타입의 관계를 UML로 표현하면 컴포지션(composition) 관계가 됩니다.
하이버네이트는 임베디드 타입을 컴포넌트(components)라 합니다.
isWork()
메서드처럼 해당 값 타임만 사용하는 의미있는 메서드를 만들 수 있습니다.임베디드 타입 덕분에 객체와 테이블을 아주 세밀하 매핑하는 것이 가능합니다.
잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많습니다.
@Entity
public class Member {
@Embedded
Address address; // 임베디드 타입 포함
@Embedded
PhoneNumber phoneNumber; // 임베디드 타입 포함
// ...
}
@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;
// ...
}
임베디드 타입에 정의한 매핑정보를 재정의하려면 엔티티에 @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),
...
)
임베디드 타입이 null 이면, 매핑한 컬럼 값은 모두 null이 됩니다.
member.setAddress(null); // null 입력
em.persist(member);
회원 테이블의 주소와 관련된 CITY, STREET, ZIPCODE 컬럼 값은 모두 null이 됩니다.