아이돔에서 팀캘린더의 참여자들 관리하기 위해 별도의 테이블로 분리하지 않고, @ElementCollection
과 @CollectionTable
을 사용하여 관리하는 것으로 설계했습니다. 하지만 이러한 선택은 무지의 상태에서 이루어졌기 때문에, 서비스를 유지하면서 성능을 향상시키는 데에 많은 제약사항을 마주 하게되었습니다. 따라서 @ElementCollection
과 @CollectionTable
가 개념과 장단점과 제가 마주한 문제에 대해서 이야기하려고 합니다.
값 타입을 컬렉션에 담아서 사용하는 것을 말합니다. 테이블은 자료구조(컬렉션)을 가질 수 없습니다. 그렇기 때문에 별도의 테이블을 구성해야 합니다. 그러나 값타입 테이블은 본인이 속한 엔티티에 ‘종속’ 됩니다. 엔티티와 엔티티의 종속은 영속성 전이와 고아객체 제거로 구현되지만 값타입 컬렉션은 자동으로 종속설정이 이루어집니다.
값 타입 컬렉션을 매핑할 때 사용합니다.
값 타입 컬렉션 객체임을 JPA가 알 수 있게 해주는 어노테이션입니다.
엔티티가 아닌 값 타입, 임베디드 타입에 대한 테이블을 생성하고 1:M
관계로 매핑됩니다.
이때, @Entity가 아닌 Basic Type이나 Embeddable Class로 정의된 컬렉션을 테이블로 생성하며 1:M 관계로 다룹니다.
@ElementCollection
과 함께 사용됩니다.
값 타입 컬렉션을 매핑할 테이블에 대한 역할을 지정하는데 사용합니다.(테이블의 이름과 조인 정보를 적어주어야 합니다.)
만약 이를 생략한다면 기본값을 이용하여 매핑합니다.
기본값 : {엔티티이름}_{컬렉션 필드 이름}
@Entity
public class TeamCalendar {
@Id
@GeneratedValue(strategy = GenerationTypy.IDENTITY)
private Long id;
@Embedded
private Participants participants;
}
@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Participants {
@ElementCollection(fetch = FetchType.LAZY)
// String인 경우에 한해서 예외적으로 허용, 이외 타입은 @AttributeOverride를 사용해서 테이블 속성을 재정의한다.
@Column(name = "PARTICIPANT")
private Set<String> participants = new HashSet<>();
//Embedded type
@ElementCollection
@CollectionTable(name = "participant", joinColumns = @JoinColumn(name = "team_calendar_id"))
private Set<Participant> participants = new HashSet<>();
public Participants(final Set<Participant> participants) {
this.participants = participants;
}
}
@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "memberId")
public class Participant implements Comparable<Participant> {
@Column(nullable = false)
private Long memberId;
}
영속성 전이(Cascade)와 고아 객체 제거
기능을 필수로 가진 것과 비슷하다고 볼 수 있습니다.지연로딩
으로 작동합니다.우리는 SQL 쿼리가 삭제할 때 delete 쿼리가 1개 insert 쿼리가 1개 나가는 것을 기대합니다. 그러나 값 타입 컬렉션은 주인 엔티티와 연관된 모든 데이터를 삭제하고 다시 저장합니다. 따라서 주인 엔티티와 관련된 엔티티의 데이터가 5개가 있다면, delete 쿼리가 5개가 나가고, 주인 엔티티가 삭제됩니다. 즉 쿼리가 6개가 나갑니다.
값 타입 컬렉션을 사용하는 것 대신에 엔티티를 사용하여 1:M 관계를 맺는 것 입니다.
아이돔에서 팀 캘린더마다 참여하는 참여자들을 등록하는 요구사항이 있었습니다. 팀 일정에 참여하는 멤버들을 등록하는 요구사항이 있었습니다. 그러나 회원이 팀에서 나가거나 탈퇴할 경우, 해당 회원이 등록되어 있는 팀 캘린더 참여자 목록에서 삭제되어야 했습니다. 불행하게도, 미숙한 상태에서 이 부분을 설계하면서 값 타입 컬렉션을 사용하게 되었습니다. 그 결과, 회원이 참여한 팀 일정의 수만큼 삭제 쿼리가 발생하였고, 벌크 연산 또한 할 수 없었습니다. 이에 대응하기 위해 엔티티로 변경하는 방법을 고려했지만, 그에 따른 코스트가 크고 이 부분이 자주 발생하지 않기 때문에 그대로 유지하기로 결정했습니다.
- 참고 자료