Notice
Recent Posts
Recent Comments
Link
관리 메뉴

look-forest

값 타입 본문

JPA/JPA

값 타입

studyHub 2024. 9. 12. 23:13

JPA의 데이터 타입 분류

  • 엔티티 타입
    • 식별자O  (데이터가 변해도 식별자로 지속해서 추적 가능)
    • 생명 주기 관리
    • 공유
  • 값 타입
    • int, Integer, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
    • 식별자X  (고유 식별자가 없고 값만 있으므로 변경시 추적 불가)
    • 생명 주기를 엔티티에 의존
    • 공유하지 않는 것이 안전(복사해서 사용 or 참조 타입일 경우 불변 객체로)

값 타입은 정말 값 타입이라 판단될 때만 사용. 엔티티와 값 타입을 혼동해서 엔티티를 값 타입으로 만들면 안됨.

식별자가 필요하고, 지속해서 값을 추적, 변경해야 한다면 그것은 값 타입이 아닌 엔티티

 

값 타입을 사용하면 도메인을 모델링하기에 용이하고, (용어 정의가 되니까)

validation rule을 한 곳에서 모아서 관리할 수 있고 편의 메서드도 생성 가능해서 좀 더 객체지향적이다.

 

값 타입 분류

  • 기본 값 타입  - 기본 타입은 값을 복사하므로 공유되지 않음. 래퍼, String은 공유 가능하지만 변경 불가.
  • 임베디드 타입(embedded type, 복합 값 타입) - 좌표, 주소 등 새로운 값 타입을 직접 정의할 수 있음. '값'들의 묶음.
  • 컬렉션 값 타입(collection value type) - 기본값, 임베디트 타입 등을 저장한 컬렉션

임베디드 타입은 자바에서 사용하는 엔티티의 값일 뿐이다. 임베디드 타입을 사용하기 전과 후에 매핑하는 테이블은 같다.

 

임베디드 타입 사용법

  • @Embeddable: 값 타입을 정의하는 곳에 표시.
  • @Embedded: 값 타입을 사용하는 곳에 표시

도메인을 모델링하기 좋다. 재사용, 메소드 활용 등 장점이 많다.
기본 생성자 필수

한 엔티티에서 같은 값 타입을 사용하면 컬럼 명이 중복된다.

@AttributeOverrides, @AttributeOverride를 사용해서 컬러 명 속성을 재정의하면 된다.

 

참고로 임베디드 타입은 속성으로 엔티티를 가질 수 있다.

FK만 가지고 있으면 된다.

 


값 타입과 불변 객체

값 객체의 가치 => 복잡성을 낮춰줌. 단순함 

  • 값 타입은 복잡한 객체 세상을 조금이라도 단순화하려고 만든 개념이다. 따라서 값 타입은 단순하고 안전하게 다룰 수 있어야 한다. -> 그래서 래퍼 클래스나 String은 불변으로 만든 것이다.
  • 객체를 추적할 필요없이 값만 같으면 동일한 객체. (ex.같은 지폐일 필요는 없고 같은 액수면 된다)  
    => 값 객체가 트래킹할 필요도 없고 복잡성을 낮춰줘서 단순하고 간편하다. 
  • Primitive 타입의 개념을 명시적으로 드러내서 복잡성을 감소시키기 위해 사용
  • 참조 객체에서 복잡하거나 중복되는 코드 그룹을 값 객체로 이동하기 위해 사용

 

값 타입 공유 참조

임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험하다. (고유 식별자가 없어서 추적이 안됨)

대신 값(인스턴스)은 복사해서 사용해야 한다.

기본 타입은 값을 복사하지만 객체 타입은 참조를 전달하므로 공유 후 변경 시 모든 사용처에서 변경되어서 위험하다.

 

객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단할 수 있다. (setter x)

값 타입은 불변 객체(immutable object)로 설계해야 한다. (생성 시점 이후 절대 값을 변경할 수 없는 객체)

불변이라는 작은 제약으로 부작용이라는 큰 재앙을 막을 수 있다.

 

+ 값은 변경하는게 아니고 새로 갈아끼워야 한다. "string1" -> "string2"는 값이 변경됐다기 보다는 갈아끼워진 것이다.

HomeAddress 하나 자체가 값이다. 내부 값만 변경할 게 아니라 값 타입 자체를 변경해야 한다. 애초에 setter를 제공하지말자.


값 타입의 비교

값 타입은 인스턴스가 달라도 그 안에 값이 같으면 같은 것으로 봐야 한다. 값이니까.

따라서 값 타입은 a.equals(b)를 사용해서 동등성 비교를 해야 한다. (값 타입의 equals() 메소드를 적절하게 재정의)

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

값 타입 컬렉션 (@ElementCollection)

값 타입을 하나 이상 저장할 때 사용 . List<엔티티> 처럼 엔티티가 아니라 값 타입을 컬렉션에 넣고 쓰는 것.

엔티티는 테이블 연관관계를 맺는데, 값 타입 리스트는 어떻게?   DB는 값을 컬렉션으로 넣을 수 없거든..

=> 별도의 테이블로 뽑아야 한다.

고유 식별자가 있으면 엔티티가 돼버린다. 값들을 묶어 PK로 구성. 단순한 값이니까.
@ElementCollection, @CollectionTable 사용해 컬렉션을 저장하기 위한 별도의 테이블 지정. FK는 소속 엔티티의 Id.

 

값 타입 컬렉션 저장

MEMBER만 저장해도 FAVORITE_FOOD, ADDRESS 테이블에도 INSERT가 일어난다.

 

값 타입 컬렉션은 CascadeType.ALL + orphanRemoval=true 설정을 포함.

값이니까 라이프사이클을 따를 수 밖에.

 

 

값 타입 컬렉션 조회

지연 로딩 전략 사용

 

 

값 타입 컬렉션 변경의 문제점

값 타입은 엔티티와 다르게 고유 식별자 개념이 없어서 값을 변경하면 추적이 어렵다.

값 타입 컬렉션에 요소를 추가하거나 변경 사항이 발생하면, 식별자가 없어서 주 엔티티와 연관된 모든 데이터를 메모리에 로딩하고, DB에서 모든 데이터를 삭제하고, 해당하는 값을 전부 다시 저장한다...

변경을 위해 위 코드를 실행하면 delete from address where member_id=? 실행 후 적절한 값들만 insert로 다시 넣는다.

 

게다가 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 하므로 =>  null 입력X, 중복 저장X

 

 

값 타입 컬렉션 대안

실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 위한 엔티티를 만들고, 여기에서 값 타입을 사용해라.

엔티티에 영속성 전이(Cascade) + 고아 객체 제거를 사용해서 값 타입 컬렉션 처럼 사용하면 된다.

(값 타입 컬렉션은 정말 단순하고 변경 추적이 필요없을 때만 사용해라)

식별자를 두고 값 타입을 속성으로 사용. 임베디드 타입은 아니고..
주 도메인 객체에서 엔티티 리스트를 일대다로 사용. 값 객체처럼 라이프 사이클을 주 도메인 객체로 맞춘다.

 

참고: 임베티드 타입과 값 타입을 속성으로 사용하는 엔티티의 차이
@Embedded는 필드를 포함하는 엔티티와 동일한 테이블에 저장되는 반면,
엔티티(Entity)는 별도의 테이블로 관리되며, 더 복잡한 관계와 상태 관리를 위해 사용. @Entity로 정의된 클래스는 각각의 객체가 데이터베이스의 행을 나타낸다. 이는 독립된 테이블로 매핑되어 관리됨.

 

Element Collection 정리

1. 가능한 Entity를 만들어 일대다 관계로 사용하자. (+영속성 전이, 고아 객체 제거)

2. 단순하다면 사용해도 된다. 다만 기본키가 없어 변경 시 추적이 어렵고, 모든 값이 PK가 되어 생기는 문제를 알고 있자.

 


참고 자료 & 이미지 출처
자바 ORM 표준 JPA 프로그래밍 - 기본편 (김영한 님)
JPA 기반의 애플리케이션 설계 (조영호 님)


 

'JPA > JPA' 카테고리의 다른 글

객체지향 쿼리 언어(JPQL) - 중급 문법  (1) 2024.09.16
객체지향 쿼리 언어(JPQL) - 기본 문법  (0) 2024.09.15
프록시와 연관관계 관리  (3) 2024.09.10
고급 매핑  (0) 2024.09.10
다양한 연관 관계 매핑  (0) 2024.09.09