@Embedded란?
새로운 값 타입을 직접 정의하여 사용할 수 있는데, JPA는 이것을 임베디드 타입(Embedded type)이라 한다.
- @Embeddable: 값 타입을 정의하는 곳에 붙인다.
- @Embedded: 값 타입을 사용하는 곳에 붙인다.
- 임베디드 타입은 기본 생성자가 필수로 있어야 한다.
엔티티에 임베디드 타입을 사용하지 않았을 때 다음과 같을 것이다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
// 근무 기간
LocalDateTime startDate;
LocalDateTime endDate;
// 주소
private String city;
private String street;
private String zipcode;
// ...
}
위와 같이 Member 객체는 근무 기간과 주소에 대한 각각의 데이터들이 모여 있으며, 하나의 객체가 모든 정보를 가지게 되는 문제가 있다.
Member 객체가 상세한 데이터를 모두 가지고 있는 것은 객체지향적이지 못하며, 응집력을 떨어뜨릴 수 있다.
임베디드 타입을 이를 객체지향에 맞는 설계를 도와준다.
@Embeddable
public class Peroid {
private LocalDateTime startDate;
private LocalDateTime endDate;
...
public Peroid(){}
}
@Embeddable
public class Address {
@Column(name="city") // 매핑할 컬럼 정의 가능
private String city;
private String street;
private String zipcode;
...
public Address(){}
}
위와 같이 각각의 근무 기간과 주소를 담은 객체를 엔티티 필드에 넣어주고 @Embedded 걸어주면 된다.
@Entity
public class Member {
@Id @GeneratedVAlue
private Long id;
private String name;
@Embedded
private Period period;
@Embedded
private Address address;
}
임베디드 타입은 값 타입의 일부이기 때문에 실제 DB는 임베디드 타입을 활용하든 하지 않든 같은 테이블 구조를 가진다.
@AttributeOverride를 사용한 속성 재정의
예를들어 Member 객체에 집과 회사의 주소를 필요로 한다면 다음과 같이 @AttributeOverride를 사용하면 된다.
@Entity
public class Member{
@Id @GeneratedValue
private Long id;
private String name;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "HOME_CITY")),
@AttributeOverride(name = "street", column = @Column(name = "HOME_STREET")),
@AttributeOverride(name = "zipcode", column = @Column(name = "HOME_ZIP"))
})
private 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_ZIP"))
})
private Address companyAddress;
}
임베디드 타입 공유 참조 주의
임베디드 타입은 여러 엔티티와 공유하는 경우 위험할 수 있다.
임베디드 타입 객체 공유하는 어떤 객체의 값을 수정하는 경우 다른 객체의 값까지 바뀔 수 있는 문제가 발생할 수 있다.
Member 객체에 임베디드 타입 Adress 객체를 각자 저장한다고 가정해보자.
이때 member1은 서울 → 대전으로 주소를 변경하려고 한다.
Address address = new Address("서울", "도로", "10-10");
Member member1 = new Member();
member1.setAddress(address);
em.persist(member1);
Member member2 = new Member();
member2.setAddress(address); //address 공유
em.persist(member2);
member1.getAddress().setCity("대전");
member1은 대전, member2는 서울이 주소에 들어가길 바랬으나, DB에서는 member1과 member2의 주소가 대전으로 변경되게 된다. 이는 같은 트랜잭션 범위 내에 공유되기 때문이다.
불변 객체로 만들기
객체를 불변하게 만들면 값을 수정할 수 없기 때문에 공유 참조로 발생하는 문제를 해결할 수 있다.
따라서 값 타입은 되도록 불변 객체 즉, 생성 시점 이후 값을 변경할 수 없는 객체로 설계해야 한다.
기존 객체를 수정하는 방식이 아닌 새로운 객체를 생성해서 값을 넣어주는 방식은 다음과 같다.
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
protected Address(){}
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
// 수정자는 만들지 않는다.
}
위와 같이 Address 객체는 생성 시점부터 변경할 수 없기 때문에 불변 객체이다.
참고
- @Inheritance는 다형성을 위한 상속
- @MappedSuperclass는 코드 재사용을 위한 상속
- @Embedded, @Embeddable은 합성을 구현할 때 사용
reference
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
'Spring > JPA' 카테고리의 다른 글
[JPA] 즉시 로딩과 지연 로딩 (0) | 2023.01.12 |
---|---|
[JPA] 프록시(Proxy) (0) | 2023.01.12 |
[JPA] @MappedSuperclass (0) | 2023.01.12 |
[JPA] 상속관계 매핑 (0) | 2023.01.11 |
[JPA] 연관관계 매핑 (다중성) (0) | 2023.01.11 |