값타입
JPA의 데이터 타입 분류
JPA에는 크게 두개의 데이터 타입이 있어!! 바로 엔티티타입과 값타입이야.
엔티티 타입
- @Entity로 정의하는 객체!
- 엔티티는 데이터가 변해도 식별자로 지속해서 추적이 가능해! 너무 당연한거지
값 타입
- int, Integer, String 처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
- 식별자가 없고 값만 있기에 변경시 추적이 불가능 해!
갑 타입에 대해 알아보자!!
값 타입
값 타입에 대해 먼저 알아보자.
값 타입의 분류
기본값 타입
- 자바 기본 타입(int,double), 래퍼 클래스(Integer, Long), String이 있어
- ex) String name, int age 이런 것들 말야!!
- 생명주기는 당연히 해당 값타입을 가지고 있는 엔티티에 의존해! ( ex. 회원을 삭제하면 이름, 나이 필드도 함께 삭제되는 그런거! )
- 값 타입은 공유하면 안돼!! 너무 당연한거지!! 예를들어, 한 회원 이름 변경시 다른 회원의 이름이 같이 변경되는 그런 상황을 상상해봐..
- 자바 기본 타입(int, double)은 항상 값을 복사해! 그렇기에 공유할 수 있는 방법이 없어!! 그렇지만 래퍼클래스나 String 같은 특수한 클래스는 공유 가능한 객체이지만 변경이 안돼!! immutable하다 이말이야!!
임베디드 타입(복합 값 타입)
- jpa에서는 새로운 값 타입을 직접 정의할 수 있어!! 이를 임베디드 타입(embedded type) 이라고 해! (주로 기본 값 타입을 모아서 만들어서 복합 값 타입이라고도 한데!!)
- @Embeddable 어노테이션을 값 타입을 정의하는 곳에 표시해!
- @Embedded 어노테이션을 값 타입을 사용하는 곳에 표시해!!
- 참고로 @Embedded는 굳이 안써도 되지만 안전하게 둘 다 작성하는 습관을 가지자!!
- 기본 생성자는 필수로 만들어줘야해!!
- 이렇게 임베디드 타입을 정의함으로써 재사용, 높은 응집도, 의미있는 메소드 뽑아서 사용 등 여러 장점을 가질 수 있어
- 당연한 것이지만 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존해!!
- 임베디드 타입을 사용한다고해서 테이블과의 매핑이 달라지지는 않아!! 테이블은 그대로!!
- 만약 한 엔티티에서 같은 값 타입을 사용한다면?? ex) Address 주소1, Address 주소2 이런 식일 경우 jpa 입장에서는 column 명이 중복이 되어버리자나!! 이때는 @AttributeOverrides, 또는 @AttributeOverride를 사용해서 컬럼 명 속성을 재정의 해주면 돼!!
- 임베디드 타입의 값이 null 이면 매핑한 컬럼 값은 모두 null 이야!! 예를들어, 특정 엔티티의 임베디드 타입 Address = null 이라면 Address 임베디드 타입을 이루고 있는 모든 값 타입 (String housecode … 등등..)도 null로 들어간다는 얘끼야
- 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 매우 dangerous → 복사해서 사용해!! 수정이 필요하면 새로 만들면서 수정되는 부분만 갈아끼우는 방식으로!
- 위의 문제를 막기 위해 불변 객체(immutable object)로 설계하자!! 대표적으로 생성자로만 값을 설정하고 수정자를 만들지 않으면 돼!!
- 비교를 할 때에는 보통 동등성(equivalence)를 비교하겠지! 그럴 때에는 equals를 사용해야하고 이를 위해 값 타입의 equals() 메소드를 적절히 재정의 해줘야해!! 자바 기본이지 이건!! (참고로 동일성은 identity이고 == 을 사용하지)
값 타입 컬렉션
- 이건 값 타입을 하나 이상 저장할 때 사용해
- @ElementCollection, @CollectionTable을 사용해!!
- 조금 생각해보면 알 수 있찌만 database는 컬렉션을 같은 테이블에 저장할 수 없고 그렇기에 이를 위한 별도의 테이블이 필요하지!!
- 그래서 결국은 값타입 컬렉션을 위한 테이블이 자동적으로 생성이 돼!!
- 값 타입 컬렉션은 지연로딩 전략을 사용하며 영속성 전이(cascade) + 고아객체 제거 기능을 필수로 가진다고 볼 수 있어!! why? 이 값 타입 컬렉션을 가지고 있는 Entity의 생명주기에 따라야하니까!!
- 하지만 이렇게해서 만들어진 값 타입은 엔티티와 다르게 식별자 개념이 없어.. → 변경이 어렵고, 값 타입 컬렉션에 변경 사항이 있을 경우 모든 데이터를 삭제하고 현재 들어있어야 할 값을 다시 저장하는 매우 비효율적인 방법으로 동작해… 그리고 이를 위한 테이블은 모든 컬럼을 묶어서 기본키를 구성하기에 null도 허용이 안돼…
- 한마디로 쓰지마!!
값 타입 컬렉션 대안
- 값 타입 컬렉션 대신에 일대다 관계를 고려해!!
- 한마디로 값타입을 엔티티로 승격시켜서 사용하자 이말이지!!
- 이때 영속성 전이(Cascade) + 고아 객체 제거를 사용해서 값 타입 컬렉션 처럼 동작하게 사용하잔거야
- 거의 보통 일대다 단방향 관계가 많이 나올텐데 이때 이전 연관관계에서도 설명했지만 외래키를 관리하는 엔티티의 필드와 실제 테이블의 외래키 위치가 다르기에 쿼리가 많이 나가는 건 어쩔 수 없어!
- 값 타입은 정말 값 타입이라 판단될 때( x,y 좌표 처럼 정말 단순 ) 사용하자!!
- 만약 식별자가 필요하고 지속해서 값을 추적하고 변경해야한다면 그것은 엔티티야!!