본문 바로가기
JPA

값 타입

by Heesu.lee 2021. 1. 19.

JPA 의 데이터 타입 분류

엔티티 타입

  • @Entity 로 정의하는 객체
  • 데이터가 변해도 식별자로 지속해서 추적 가능
  • 회원 엔티티의 키나 나이 값을 변경해도 식별자로 인식 가능

값 타입

  • int, Integer 처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
  • 식별자가 없고 값만 있으로 변경 시 추적 불가

값 타입 분류

기본값 타입

  • 자바 Primitive 타입
  • 래퍼 클래스
  • String
기본값 타입의 경우 생명주기를 엔티티에 의존한다. (회원 삭제하면 이름, 나이 필드도 함께 삭제)
값 타입의 경우 서로 다른 엔티티와 공유하면 안된다. (회원 이름이 바뀌었다고 다른 회원의 이름도 함께 변경되면 안된다.)
※ 참고 - 자바의 기본 타입은 절대 공유 되지 않는다.
int, double 과 같은 primitive 타입은 절대 공유 되지 않는다.
기본 타입은 항상 값을 복사함
(Integer 래퍼 클래스와 String 같은 특수한 클래스는 공유 가능한 객체이지만 변경 불가)

임베디드 타입

  • embedded type, 복합 값 타입
  • 여러 개 묶어 사용 시 (좌표, 주소 등)
새로운 값 타입을 직접 정의할 수 있음
JPA 는 임베디드 타입이라 함 (주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 함)
주로 기본 값 타입을 모아서 만들기 때문에 복합 값 타입이라고도 함

※ 핵심은 임베디드 타입 역시 int, String 과 같이 값 타입이라는 것 (엔티티 타입과 다르게 추적되지 않음)

 

임베디드 타입 사용법

  • @Embeddable: 값 타입을 정의하는 곳에 표시
  • @Embedded: 값 타입을 사용하는 곳에 표시
  • 기본 생성자 필수

임베디드 타입의 장점

  • 재사용
  • 높은 응집도
  • 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존함

임베디드 타입과 테이블 매핑

  • 임베디드 타입은 엔티티의 값일 뿐이다.
  • 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.
※ 참고
객체와 테이블을 아주 세밀하게 매핑하는 것이 가능
잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래스의 수가 더 많음

임베디드 타입과 연관관계

  • 임베디드 타입은 엔티티를 가질 수 있다.

만약 한 엔티티에서 같은 값 타입을 사용할 시 @AttributeOverrides, @AttributeOverride 를 사용해서 컬럼 명 속성을 재정의

  • 컬럼 명이 중복되기 때문에 재정의하여 사용해야 함

값 타입과 불변 객체

값 타입 공유 참조

  • 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함 → 이런 경우 사이드 이펙트 발생함
위와 같은 문제는 임베디드 타입의 인스턴스를 여러 엔티티에서 공유하는 경우 발생한다.
다시말해, 같은 값이더라도 새롭게 생성하여 각 엔티티에서 사용하는게 올바른 방식이다.

객체 타입의 한계

  • 객체 타입의 경우 대입 연산자를 사용하는 경우 참조 값을 대입한다.
  • 이러한 객체의 공유 참조는 피할 수 없다.
// 기본 타입
int a = 10;
int b = a; // 기본 타입은 값을 복사

b = 4; // a == 10, b== 4

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

b.setCity("New") // a 와 b 모두 같은 참조값을 가지고 있어 변경 됌

 

※ 불변 객체
값 타입의 경우 불변 객체로 설계해야함 - 사이드 이펙트 방지
생성자로만 값을 설정하고 수정자를 만들지 않으면 가능
참고 - Integer, String 은 자바가 제공하는 대표적인 불변 객체

값 타입 비교

값 타입의 경우 인스턴스가 다르더라도 그 안에 값이 같으면 같은 것으로 봐야한다.

Address a = new Address(“서울시”);
Address b = new Address(“서울시”);
// a 와 b 의 참조값은 다르더라도 같은 값

값 타입 비교 정리

  • 동일성 비교와 동등성 비교를 다르게 사용해야한다.
    • 동일성: 인스턴스의 참조값을 비교 (== 사용)
    • 동등성: 인스턴스의 값을 비교 (equals 사용)
값 타입의 경우 동등성 비교를 해야한다.
따라서 값타입의 경우 equals 메소드를 적절하게 재정의하여 사용해야한다. (주로 모든 필드 사용) + HashCode 도 재정의

값 타입 컬렉션

  • 값 타입을 하나 이상 저장할 때 사
  • @ElementCollection, @CollectionTable 사용
  • 데이터베이스는 컬렉션을 같은 테이블에 저장할 수 없다.
  • 컬렉션을 저장하기 위한 별도의 테이블이 필요함

값 타입 컬렉션 사용

  • 값 타입 저장
  • 값 타입 조회 - 지연 로딩 전략
  • 값 타입 수정 - 기존꺼를 삭제 후 새롭게 생성하여 넣어줘야 한다.

값 타입 컬렉션의 제약사항

  • 값 타입은 엔티티와 다르게 식별자 개념이 없다. (값을 변경하면 추적이 어렵다.)
  • 결국 값 타입이기에 그 생명주기는 소속된 엔티티와 동일하게 가져간다.
  • 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 함 (null 허용 x, 중복 저장 x)

값 타입 컬렉션 대안

  • 실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 고려
  • 일대다 관계를 위한 엔티티를 만들고, 여기에서 값 타입을 사용
  • 영속성 전이 + 고아 객체 제거를 사용해서 값 타입 컬렉션 처럼 사용
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns =
        @JoinColumn(name = "member_id")
)
private List<Address> addressHistory = new ArrayList<>();


@OneToMany(cacade = CascadeType.ALL, orpahanRemoval = true)
@JoinColumn(name = "member_id")
private List<AddressEntity> addressHistory = new ArrayList();
값 타입은 정말 값 타입이라 판단될 때만 사용
엔티티와 값 타입을 혼동해서 엔티티를 값 타입으로 만들면 안됨
식별자가 필요하고, 지속해서 값을 추적, 변경해야 한다면 그것 은 값 타입이 아닌 엔티티

참조

  • 해당 게시글은 김영한님의 "자바 ORM 표준 JPA 프로그래밍 - 기본편" 을 바탕으로 작성되었습니다.

'JPA' 카테고리의 다른 글

객체지향 쿼리 언어  (0) 2021.01.25
프록시와 연관관계 관리  (0) 2021.01.20
상속 관계 매핑  (0) 2021.01.14
다양한 연관관계 매핑  (0) 2021.01.12
연관관계 매핑 기초  (0) 2021.01.12

댓글