[JPA BASIC] 4. Type

값타입

값 타입은 식별자 없이 엔티티에 종속되어 값으로만 사용되는 객체이며, @Id가 붙는 식별자는 값 타입이 아니다. 엔티티에 값이 될 수 있는 것들을 알아보자!

  • 값 타입 종류
    • 기본값 타입
    • 임베디드 타입
    • 컬렉션 타입

기본 값 타입 (Primitive Type)

  • 자바의 기본 타입 int, double, boolean 등과 그에 대응하는 래퍼 클래스, String, Date 등
  • 특별한 매핑 없이 JPA가 자동으로 처리
  • 불변성 유지가 어렵고, 여러 엔티티에서 공유 시 문제가 생길 수 있음
int age;		// 자바 기본 타입(primitive type)
Integer count;	// Wrapper 클래스
String name;

여러 엔티티 공유 예시(문제가 생길 수 있음)

String address = "Seoul";
member1.setAddress(address);
member2.setAddress(address);

address = "Busan"; // 외부에서 값을 변경

임베디드 타입

  • 기본 값 타입을 모아서 복합 값 타입을 만든다.
  • 복합 키(Composite Key)를 하나의 임베디드 타입으로 정의하여, 엔티티의 식별자로 사용할 수 있다.
  • @Embeddable: 값 타입을 정의하는 곳에 표시
  • @Embedded: 값 타입을 사용하는 곳에 표시
  • 기본 생성자 필수
  • 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존함
  • 임베디드 타입의 값이 null이면 매핑한 컬럼 값은 모두 null
  • 여러 엔티티에서 공유하면 위험함
  • @Embeddable 클래스가 @EmbeddedId(복합 키)로 사용될 경우 equals()hashCode() 메소드를 반드시 재정의(override)해야 한다.(동등성 보장)

// 임베디드 타입
@Embeddable
@Getter
@EqualsAndHashCode
public class Address {
    private String city;
    private String street;
    private String zipcode;
    }
}

// 엔티티
@Entity
@Getter @Setter
public class Member {
    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String name;

    @Embedded
    private Address address;

}

@EmbeddedId(복합 키)

  • Serializable를 써줄것
  • equals()hashCode() 메소드를 반드시 재정의(롬복 사용 가능)
// 복합키 임베디드 타입
@Embeddable
@NoArgsConstructor
public class TaskId implements Serializable {
    @Column(name = "employee_id", length = 50)
    private String employeeId;

    @Column(name = "task_id", length = 11)
    private int taskId;

    public TaskId(String employeeId, int taskId) {
        this.employeeId = employeeId;
        this.taskId = taskId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TaskId taskId1 = (TaskId) o;
        return taskId == taskId1.taskId && Objects.equals(employeeId, taskId1.employeeId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(employeeId, taskId);
    }
}

// 엔티티
@Setter
@Entity
@NoArgsConstructor
public class Task {
    @EmbeddedId
    private TaskId taskId;

    @Column(name = "task_name", length = 100)
    private String taskName;

    @Column(name = "date")
    @UpdateTimestamp
    private LocalDateTime updatedDate;

}

@AttributeOverride: 속성 재정의

  • 한 엔티티에서 같은 값 타입을 사용하면?
  • 컬럼 명이 중복됨
  • @AttributeOverrides, @AttributeOverride를 사용해서 컬럼 명 속성을 재정의
@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_zipcode"))
})
private Address homeAddress;

불변 객체

  • 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단
  • 값 타입은 불변 객체(immutable object)로 설계해야함
  • 불변 객체: 생성 시점 이후 절대 값을 변경할 수 없는 객체
  • 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됨
  • 참고: Integer, String은 자바가 제공하는 대표적인 불변 객체

컬렉션 타입

  • 값 타입을 하나 이상 저장할 때 사용
  • @ElementCollection, @CollectionTable 사용
  • 데이터베이스는 컬렉션을 같은 테이블에 저장할 수 없다.
  • 컬렉션을 저장하기 위한 별도의 테이블이 필요함
  • 값 타입 컬렉션은 영속성 전에(Cascade) + 고아 객체 제거 기능을 필수로 가진다.
  • 개인적인 생각으론 컬렉션을 쓸 일이 있는지 모르겠다. 되도록 일대다 관계를 고려 하자.

© 2023 Lee. All rights reserved.