반응형
JPA 값 타입 컬렉션 완전 정리
값 타입을 Collection(Set, List) 형태로 저장하는 것
기본 관계형 DB는 컬렉션 저장을 지원하지 않는다, 그래서 이미지 처럼 멤버:FAVORITE_FOOD, 멤버:ADDRESS를 1:N구조로 매핑해야한다.
멤버 클래스
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
@Column(name = "FOOD_NAME")
private Set<String> favoriteFoods = new HashSet<>();
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private List<Address> addressHistory = new ArrayList<>();
- @ElementCollection → 값 타입 컬렉션임을 선언
- @CollectionTable → 별도 테이블과 조인 매핑
- @Column → 단일 값 타입일 경우 컬럼명 지정 가능
- 관계형 데이터베이스는 객체 컬렉션을 하나의 테이블에 바로 저장할 수 없음 → 값 타입 컬렉션은 별도의 테이블에 저장됨
더보기
PostgreSQL의 ARRAY 타입은?
PostgreSQL 등은 ARRAY 컬럼을 지원하지만, JPA에서 매핑하려면 커스텀 @Type, UserType 구현이 필요하고 포팅성, 관리 이슈로 권장되지 않음.
→ JPA는 기본적으로 RDB의 정규화 방식에 맞춰 설계하는 것이 좋음
특징
- 멤버를 persist할때 insert쿼리는 컬렉션의 add한 수만큼 발생한다.
- 멤버만 persist해도 컬렉션 insert쿼리가 발생한다 (이유 = 값타입 이므로 member의 라이프사이클에 의존한다, 즉 멤버의 변경에 따라 달라진다)
- 값 타입 컬렉션은 영속성 전이 + 고아객체 제거 기능을 필수로 가진다고 볼 수 있다
- 멤버 조회시, 값 타입 컬렉션은 가져오지 않는다 (지연로딩)
Set<String> food 바꾸려면 어케함?
member.getFavoriteFoods().remove("치킨");
member.getFavoriteFoods().add("한식"); // 이렇게 바꿀 수 있음
값 타입 컬렉션의 단점
값 타입 컬렉션은 변경 추적이 불가능합니다. 예를 들어:
member.getAddressHistory().remove(oldAddress);
member.getAddressHistory().add(newAddress);
위처럼 변경하면:
- 기존 관련 row 전체 delete
- 현재 리스트의 모든 값 bulk insert
즉, 1개 바꿨는데도 전체 delete + insert 발생 → 성능 저하 + Audit 추적 어려움
실무 대안: 일대다 매핑을 이용한 값 타입 유사 모델링
@Entity
public class Member {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID") // 단방향 매핑
private List<AddressEntity> addressHistory = new ArrayList<>();
}
@Entity
public class AddressEntity {
@Id @GeneratedValue
private Long id;
@Embedded
private Address address; // 불변 값 타입으로 구성
}
반응형
'프로그래밍 > JPA' 카테고리의 다른 글
[JPA] JPQL 기본 문법과 기능 (2) | 2025.07.30 |
---|---|
[JPA] JPQL 소개 (2) | 2025.07.30 |
[JPA] 값 타입과 불변 객체 (2) | 2025.07.23 |
[JPA] 값 타입 (1) | 2025.07.22 |
[JPA] 영속성 전이 CASCADE (2) | 2025.07.17 |