[JPA] 값 타입 (JPA 기본편 by 김영한)

kimdoha·2023년 8월 30일
1

[JPA]

목록 보기
8/8
post-thumbnail

값 타입

JPA의 데이터 타입은 2가지가 있습니다.

  1. 엔티티(Entity) 타입

    • @Entity로 정의하는 객체
    • 데이터가 변해도 식별자를 통해 지속해서 추적 가능
  2. 값 타입

    • 단순히 값으로 사용하는 자바 기본 타입이나 객체
    • 식별자가 없고 값만 있으므로 변경 시 추적 불가
    • 값 타입을 소유한 Entity에 생명주기를 의존
    • 기본 값 타입, Embedded 타입, Collection 값 타입 등으로 분류

기본 값 타입

int age;		// 자바 기본 타입(primitive type)
Integer count;	// Wrapper 클래스
String name;

기본 값 타입에는 자바 기본 타입, 래퍼(Wrapper) 클래스, String 등이 포함되고, 기본 값 타입의 생명주기는 Entity에 의존합니다. 예를 들어 회원을 삭제하면 회원의 이름, 나이 Field도 함께 삭제됩니다.

자바의 기본 타입은 절대 공유해서는 안되며 항상 값을 복사하도록 동작합니다. Integer 같은 래퍼 클래스나 String 같은 특수한 클래스는 공유 가능한 객체이지만 불변 객체로서 한 번 만들어진 객체는 데이터 수정이 불가합니다.

임베디드 타입(복합 값 타입)

주로 기본 값 타입을 모아서 새로운 값 타입을 직접 정의 하는 것으로 재사용성이 높습니다. 아래의 회원 엔티티에서 기본 값 타입을 모아서 임베디드 타입으로 리팩토링 하면 다음과 같습니다.

회원 엔티티는 이름, 근무 시작일, 근무 종료일, 주소 도시, 주소 번지, 주소 우편번호를 가진다.
Embbeded-Before

회원 엔티티는 이름, 근무 기간, 집 주소를 가진다.

Embedded-After

Member-Entity

  • 회원 엔티티
@Entity
public class Member {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(nullable = false)
  private String name;

  @Embedded
  private Period workPeriod;

  @Embedded
  private Address homeAddress;

}
  • Period 임베디드 타입
@Embeddable
@NoArgsConstructor
public class Period {
  private ZonedDateTime startDate;
  private ZonedDateTime endDate;
}
  • Address 임베디드 타입
@Embeddable
@NoArgsConstructor
public class Address {
  private String city;

  private String street;

  @Column(name = "zipcode")
  private String zipCode;
}
  • 예제 결과
    Embedded-Result

Embedded 값 타입을 정의하는 곳에 @Embeddable, 값 타입을 실제 사용하는 곳에는 @Embedded 표시합니다. 추가적으로 임베디드 타입의 경우 기본 생성자가 필수로 존재해야 합니다.

Embedded-Type
임베디드 타입은 엔티티의 값일 뿐이며 임베디드 타입을 사용하기 전과 후의 변함은 없어야 합니다. 또한 임베디드 타입의 값이 null이면 매핑한 컬럼 값은 모두 null이 됩니다.

한 엔티티에서 같은 값 타입을 사용하면 @AttributeOverride 를 통해 컬러 명 속성을 재정의 합니다.

  @Embedded
  private Address homeAddress;

  @Embedded
  @AttributeOverrides({
      @AttributeOverride(name = "city", column = @Column(name = "work_city")),
      @AttributeOverride(name = "street", column = @Column(name = "work_street")),
      @AttributeOverride(name = "zipCode", column = @Column(name = "work_zip_code"))
  })
  private Address companyAddress;

예제 결과
Attribute-Overrides

값 타입과 불변 객체

값 타입을 여러 Entity에서 공유 참조 하면 부작용(side effect) 발생할 수 있습니다. 자바 기본 타입은 값을 대입하면 항상 값을 복사해서 사용하지만 Embedded 타입의 경우는 직접 정의한 객체 타입으로 값을 대입하면 참조 값을 공유합니다. 객체의 공유 참조는 막을 수 없지만 공유되더라도 값을 변경하지 못하도록 불변 객체 설정을 함으로써 부작용을 막아야합니다.

// 기본 타입(primitive type)
int a = 10;
int b = a;

// 객체 타입
Address a = new Address(Old);
Address b = a; //객체 타입은 참조를 전달 
b.setCity(New)

Side-Effect-Example

Solution

불변 객체
값 타입은 생성 시점 이후 절대 값을 변경할 수 없는 객체, 즉 불변 객체로 설계합니다. 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됩니다.
ex) Integer, String 은 자바가 제공하는 대표적인 불변 객체

값 타입의 비교

값 타입은 a.equals(b) 를 사용해서 동등성 비교를 해야 함

  • 동일성(identity) 비교: 인스턴스의 참조 값을 비교, == 사용
  • 동등성(equivalence) 비교: 인스턴스의 값을 비교, equals()

값 타입 컬렉션

값 타입을 하나 이상 저장할 때, ListSet 과 같은 Collection을 사용합니다.DB는 컬렉션을 같은 테이블에 저장할 수 없기 때문에, 컬렉션을 저장하기 위한 별도의 테이블이 필요합니다.

Collction-Example

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

  @ElementCollection(fetch = FetchType.LAZY)
  @CollectionTable(name = "ADDRESS", joinColumns = {
      @JoinColumn(name = "MEMBER_ID")
  })
  private List<Address> addressHistory = new ArrayList<>();

@ElementCollection, @CollectionTable 을 통해 테이블 설정을 할 수 있지만 Collection 값 타입은 다음과 같은 제약사항으로 인해 사용하지 않은 것을 권장합니다.

  • 값 타입은 엔티티와 다르게 식별자 개념이 없다.
  • 값은 변경하면 추적이 어렵다.
  • 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장한다.
  • 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 함 (null 입력X, 중복 저장X)

Collection 값 타입 대신에 1:N 일대다 관계로 Entity를 만들고,해당 Entity에서 영속성 전이, 고아 객체 옵션과 함께 값 타입 Collection처럼 사용합니다.

실전 연습

👉 실전 예제 코드

0개의 댓글

관련 채용 정보