값 타입 컬렉션(Collection Value Type)
📌 값 타입 컬렉션(Collection Value Type)
값 타입을 하나 이상 저장할 때 사용한다.
데이터베이스는 컬렉션을 같은 테이블에 저장하지 않기 때문에, 실행하면 컬렉션을 저장하기 위한 별도의 테이블이 생성된다.
📌 사용 방법
🔎 저장
서로 다른 테이블임에도 불구하고 Member만 persist해도 함께 저장되었다.
그 이유는 값 타입은 Entity의 생명 주기에 의존하기 때문에 Entity를 저장하면 함께 저장되기 때문이다.
💡 값 타입 컬렉션은 영속성 전이 + 고아 객체 제거 기능을 필수로 가진다.
🔎 조회
값 타입 컬렉션은 기본적으로 지연 로딩 전략을 사용한다.
이렇게 지연 로딩을 사용하는 이유는 @ElementCollection의 FetchType이 LAZY가 기본으로 되어 있기 때문이다.
✔ 직접 조회
컬렉션 값 타입을 직접 조회하려면 아래와 같이 하면 된다.
🔎 수정
값 타입은 불변 객체여야 하므로, 최초 생성자를 통한 입력 후 값을 마음대로 변경하지 못하도록 해야 한다.
private로 수정자를 막거나, 인스턴스를 교체하는 방식으로 변경해야 한다.
또 다른 방법은 컬렉션에서 특정 값을 검색하여 삭제 후 추가하는 것이다.
기본적으로 컬렉션은 equals()를 통해 객체를 찾기 때문에 equals()를 동등성 비교를 하도록 미리 오버라이딩해야 한다.
다음 글의 값 타입의 비교 부분 참고
https://dev-rootable.tistory.com/100
📌 제약 사항
- 값 타입은 Entity와 다르게 식별자 개념이 없어 추적이 어렵다.
- 값 타입 컬렉션에 변경 사항이 발생하면 주인 Entity와 연관된 모든 데이터를 삭제하고 값 타입 컬렉션에 있는 현재 값들을 모두 다시 저장한다.
- 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본 키를 구성해야 한다.
- NULL 입력 x, 중복 저장 x
🔎 대안
실무에서는 상황에 따라 값 타입 컬렉션 대신 일대다 관계를 고려한다. 일대다 관계를 위한 엔티티를 만들고, 아래와 같은 wrapping 작업을 하면 Address 임베디드
값 타입이 아닌 엔티티와 관계를 맺게 된다. 또한 cascade와 orphanRemoval 속성을 통해 값 타입 컬렉션이 가졌던 기본 속성을 그대로 살릴 수 있다.
이렇게 하면 엔티티와 관계를 맺기 때문에 쿼리 최적화하기 용이하고, 활용도가 높아진다.
식별자가 필요하고 지속해서 값을 추적, 변경해야 한다면 값 타입이 아닌 엔티티
Reference:
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard