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 |
댓글