JPA

값 타입 Value Object

유휴 2022. 12. 4. 19:03

강의참고 - 자바 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<>();
}