CH09 값 타입

값 타입이 뭔가 싶지만 초반에 엔티티 매핑에서 테이블 단위의 매핑을 했다면 이번에는 컬럼 단위의 매핑에 관한 내용이었다. 어떻게 매핑한다는 내용보다는 Object 관점에서 일반적으로 사용하는 컬럼의 타입 말고 조금 더 세밀한 객체 모델링을 할 수 있는 방법에 대해서 다뤘다.

실무에서 많이 사용해온 부분이라서 엄청 자세하게 정리할 필요까지는 없을 것 같지만 기본적인 내용들은 정리를 하려고 한다.

@Embedded, @Embeddable

public class Member {

	@Id @GeneratedValue
    	@Column(name = "MEMBER_ID")
    	private Long id;

    	@Column(name = "USERNAME")
    	private String username;
        
        //period
    	@Embedded
    	private Period period;

    	//address
    	@Embedded
    	private Address homeAddress;
        
 }
@Embeddable
public class Address {

    private String city;
    private String street;
    private String zipcode;

    public Address() { // 기본생성자 필수 
    }
    
}

@Embeddable
public class Period {

    private LocalDateTime startDate;
    private LocalDateTime endDate;

    public Period() { // 기본생성자 필수 
    }
    
}

당연한 이야기지만 @Embeddable 도 엔티티와 관계를 맺을 수 있다. 결국 @Embeddable 의 필드들이 @Embedded 가 선언된 곳에 귀속 되므로 @Embeddable 에 @OneToMany 등 엔티티와 관계 설정을 하는 행위 자체가 애초에 @Embedded 에 관계 설정을 한 것과 동일하다.

@AttributeOverride 로 속성을 아래와 같이 재정의도 할 수 있다.

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Setter
@Getter
public class Member{

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    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.zip", column = @Column(name = "HOME_ZIP")),
            @AttributeOverride(name = "zipcode.plusFour", column = @Column(name = "HOME_PLUS_FOUR")),
    })
    private Address homeAddress;


    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "city", column = @Column(name = "COMPANY_CITY")),
            @AttributeOverride(name = "street", column = @Column(name = "COMPANY_STREET")),
            @AttributeOverride(name = "zipcode.zip", column = @Column(name = "COMPANY_ZIP")),
            @AttributeOverride(name = "zipcode.plusFour", column = @Column(name = "COMPANY_PLUS_FOUR")),
    })
    private Address companyAddress;


}
@Embeddable
public class Address {

    private String city;
    private String street;

    @Embedded
    private Zipcode zipcode;
}
@Embeddable
public class Zipcode {

    private String zip;
    private String plusFour;
}

Value Object

앞서 정리한 @Embeddable 도 그렇고 DDD 에서 말하는 애그리거트를 구성하는 엔티티와 밸류 객체 모두 결국 마틴 파울러가 말하는 Value Object 로 구현하는 것이 이상적이다. 책에는 불변 객체의 이점에 대해서 설명되어 있는데, Value Object 가 불변으로 설계가 되어있다.

정리가 잘 된 포스팅들이 여럿 있는데 이 포스팅이 보기가 편한 것 같아서 링크를 남긴다. 필드들을 묶을 수 있는 논리적 단위가 있다면 Value Object 로 만들어서 묶어 주는 것이 더 세밀한 객체 모델링에 가까워지는 것이라고 생각 된다. 생성시 validation 도 각 객체에서 책임을 더 세밀하게 나눠서 처리할 수 있고, 불변 객체로 만들어서 객체를 더 신뢰하고 사용할 수 있다.

JAVA RECORD

자바 14에 프리뷰로 공개되고 16부터 정식 스펙이 된 record 가 Value Object 의 조건들을 만족시킨다. 즉, Value Object 를 손쉽게 만드는 방법으로 자바의 record 를 사용할 수 있다. 관련된 포스팅이 있어 링크를 남긴다.

값 타입 컬렉션(@ElementCollection, @CollectionTable)

@ElementCollection
 @CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
 @Column(name = "FOOD_NAME")
 private Set<String> favoriteFoods = new HashSet<>();

값 타입 컬렉션의 제약사항은 각 row 가 pk 를 기준으로 구분되어 인식되는 개체가 아니기 때문에 데이터 하나만 변경이 발생해도 모두 다 지우고 새로 insert 하는 식으로 동작한다는 것이다.

개인적인 경험 내에서는 실무에서 사용하는 경우를 보지는 못했다.

Last updated