값 타입 Value Object
강의참고 - 자바 ORM 표준 JPA 프로그래밍 - 기본편
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
목표
- JPA의 데이터 타입을 알아본다.
- 값타입을 분류하고 각 타입별 특성 및 구현방법을 알아본다.
- 불변 객체에 대해서 알아본다.
JPA의 데이터 타입 분류
- 엔티티 타입
* @Entity로 정의하는 객체
* 데이터가 변해도 식별자(id)를 통해 지속해서 추적 가능하다.
- 값 타입
* int, Integer, String 처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
* 식별자가 없으므로 변경시 추적 불가능하다.
값 타입 분류
- 기본 값 타입 (자바 기본 타입, Wrapper Class, String)
- 임베디드 타입(복합 값 타입)
- 컬렉션 값 타입
기본 값 타입
- int, Integer, String 처럼 단순히 값으로 사용하는 자바 기본 타입이나 Wrapper Class를 말한다.
- 생명주기를 엔티티에 의존한다.
임베디드 타입
- 기본 값 타입을 모아서 직접 정의한 복합 값 타입을 말한다.
임베디드 타입 어노테이션
- 값 타입을 사용하는 곳에 @Embedded 어노테이션을 표기한다.
- 값 타입을 정의하는 곳에 @Embeddable 어노테이션을 표기한다.
- 한 엔티티에서 같은 값 타입을 사용하는경우 @AttributeOverrides, @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 address;
}
@Getter
@AllArgsConstructor
@Embeddable //값 타입을 정의하는 곳에 표기
public class Address{
private String city;
private String street;
private String zipcode;
}
값 타입과 불변객체
- 자바의 기본타입은 공유가 불가능하다.
- 기본타입의 wrapper 클래스, String 클래스는 공유가 가능하지만 변경이 불가능하다.
- 하지만 임베디드 타입의 경우 서로 다른 엔티티 객체에서 공유가 가능하며 변경도 가능하다.
- 공유된 값타입이 변경되는 경우 예상하지 못한 값 수정이 발생 할 수 있다.
- 따라서 임베디드 타입의 값타입은 변경 불가능한 불변객체로 구현해야한다.
- 불변객체 : 생성 시점 이후 값을 변경할 수 없는 객체이다.
* Setter를 구현하지 않고 파라미터를 받는 생성자만을 통해 객체를 생성가능하도록 설계한다.
값 타입 컬렉션
- 값 타입을 하나 이상 저장할 때 사용한다. (1:N)
- 데이터베이스는 1:N 관계의 컬렉션을 같은 테이블에 저장할 수 없으며 컬렉션을 저장하기 위한 별도의 테이블이 필요하다.
- 값 타입 컬렉션에는 식별자가 없기 때문에 엔티티의 생명주기와 동일한 생명주기를 가진다.
- 즉 영속성전이(cascade)와 고아객체제거(orphanRemoval) 기능을 가지고있다.
값타입 컬렉션 어노테이션
- @ElementCollection : 해당 필드가 값타입 컬렉션임을 알려준다.
- @CollectionTable : 값 타입 컬렉션을 저장할 테이블을 명시해준다.
값타입 컬렉션 구현코드
@Entity
public class Member {
@Id
@generatedValue
@Column(name="member_id")
private Long id;
private String name;
@ElementCollection // 값타입 컬렉션임을 알려주는 어노테이션
// @CollectionTable 값타입 컬렉션을 저장할 테이블을 명시해주는 어노테이션
@CollectionTable(name="address", joinColumns=@JoinColumn(name="member_id"))
private List<Address> addresses = new ArrayList<>();
}
값 타입 컬렉션 제약사항
- 값타입은 엔티티와 다르게 식별자 개념이 없으므로 변경하면 추적이 어렵다.
- 값 타입 컬렉션에 변경 사항이 발생하면, 주인엔티티와 연관된 모든 데이터를 삭제 후 값타입 컬렉션의 현재 값을 모두 다시 저장한다.
- 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성해야한다.(null, 중복 불가)
값타입 엔티티
- 값타입 컬렉션의 제약사항으로 인하여 값타입 컬렉션을 사용하는 대신에 값타입 엔티티를 만들어 사용하는것을 고려해야한다.
- 영속성전이(cascade)와 고아객체제거(orphanRemoval) 기능을 필수로 추가해야한다. (cascade.ALL + orphanRemoval=true)
값타입 엔티티 구현코드
// 값타입 엔티티 AddressEntity를 만든다
@Entity
public class AddressEntity{
@Id
@generatedValue
@Column(name="address_id")
private Long id;
private Address address;
}
@Entity
public class Member {
@Id
@generatedValue
@Column(name="member_id")
private Long id;
private String name;
// 값타입 컬렉션 대신 값타입 엔티티 AddressEntity를 사용한다.
// 영속성전이(cascade)와 고아객체제거(orphanRemoval)기능을 추가한다.
@OneToMany(cascade="CascadeType.ALL", orphanRemoval=true)
@JoinColumn(name="member_id")
private List<AddressEntity> addresses = new ArrayList<>();
}