Trouble Shooting 기록 13. ElementCollection 대체하기 (1)
ElementCollection
- JPA에서 값타입을 컬렉션에 담아서 사용하는 경우, 해당 컬렉션을
ElemenctCollection
이라고 한다. - 여기서 값타입이란 Integer, String과 같은 java의 기본 자료형 또는 임베디드 타입을 의미한다.
- 현재 진행 중인 프로젝트에서 매장(Store)의 휴무일(offDays)을 ElementCollection으로 사용하고 있다.
@Entity
public class Store extends BaseEntity {
// ...
@ElementCollection(fetch = FetchType.LAZY) // default: LAZY라 사실 명시할 필요없음
private List<String> offDays = new ArrayList<>();
// ...
}
- 위처럼 @ElementCollection 애노테이션만 붙여주면 해당 필드가 Collection이라는 것을 알게 되고, RDB에서 이 필드를 관리할 별도의 테이블을 생성해준다.
- 함께 @CollectionTable을 사용하여 매핑할 테이블의 정보를 직접 설정해줄 수도 있다.
-
보다싶이 애노테이션 하나 혹은 둘 만으로 RDB가 다루지 못하는 Colleciton 형태의 데이터를 쉽게 처리할 수 있게 도와주는 편리한 기능이다.
- 하지만 ElementCollection은 문제점을 몇가지 가지고 있고, 나 또한 이를 사용하면서 해당 문제들로 인한 성능 개선의 필요성을 크게 느꼈기에 ElementCollection을 대체하고자 한다.
ElemenctCollection의 한계
1. 엔티티가 값타입이기 때문에 식별자 개념이 없다.
식별자
가 존재하지 않기 때문에 값이 변경되는 경우, 이를 추적하는 것이 어렵다.- 사실 이 문제점으로 인한 에로사항은 크게 느끼지 못하였다. (추적한 적이 아직 없음)
- 하지만 식별자의 부재로 인한 문제점은 겪어보지 않아도 충분히 와닿는다.
2. 식별자가 별도로 없으므로 PK를 구성하는 경우 조건에 맞게 모든 컬럼을 묶어서 PK를 구성해야 한다.
- 값타입을 저장할 @Collection 테이블의 구성을 보면 아래와 같다.
- 테이블의 구성을 보면 앞서 언급한 1번의 문제대로 식별자가 존재하지 않고, 값타입을 가지고 있는 Store 엔티티의 id값 (PK)와 offDays의 값만 존재한다.
- 결국 식별자가 없기에 해당 데이터의 값이 변경되면 어떤 레코드가 변경됐는지 추적하기가 어렵다.
- 또한 PK의 조건 (Not null, Unique)을 갖추려면 id와 off_days 두 컬럼을 묶어야만 가능하므로 PK를 생성할 때 복합키 방식을 강제로 사용하여야 한다.
3. 변경 사항이 발생하면 연관 테이블의 데이터를 모두 삭제하고 남아있거나 새로 추가된 데이터를 다시 넣는다
- 무엇보다 가장 직관적이게 느껴지는 문제점은 3번 문제점인 것 같다.
- 만약 위 테이블에서 36번 Store의 offDays를 변경하는 경우, update 쿼리가 하나 날라가거나 더 많은 offDays가 들어온 경우 insert 쿼리가 함께 날라가는 것을 생각하게 된다.
- 하지만 현실은 delete 쿼리가 날라간 후, 기존의 데이터와 함께 값으로 들어온 모든 데이터를 insert 쿼리로 입력한다.
- 위처럼 delete 쿼리로 값타입이 저장된 테이블의 모든 데이터를 날린 후, insert 쿼리가 추가된 데이터의 수만큼 반복하여 날라간다.
- ex) 기존 화요일 + 수요일, 목요일인 경우 => delete후 (화, 수, 목) insert 쿼리가 총 3번 날라감.
- 이는 전혀 예상치 못한 방향으로 DB가 동작하고 있는 것이다.
@ManyToMany로 대체하자
- 따라서 값타입 자체를 엔티티로 생각하고 데이터를 처리하는 방식으로 변환하기로 마음을 먹었다.
- 강의나 기타 블로그를 보면 일대다 관계를 고려하라고 하는데, 현재 내 기능의 경우 다대다의 관계가 필요하므로 이를 사용할 것이다.
- 물론 @ManyToMany 에노테이션을 그대로 사용하는 것이 아닌 @OneToMany, @ManyToOne 관계로 풀어내어 사용한다.
변경된 엔티티 관계
- 기존의 Collection 테이블이 아닌 엔티티간의 연관 관계로 이를 풀어냈다.
@Entity
public class Store extends BaseEntity {
// ...
// Days (N:N)
@OneToMany(mappedBy = "store")
private List<DaysOfStore> daysOfStoreList = new ArrayList<>();
// ...
}
@Entity
public class DaysOfStore {
@Id
@GeneratedValue
@Column(name = "days_of_store_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "days_id")
private Days days;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "store_id")
private Store store;
// ...
}
@Entity
public class Days {
@Id
@GeneratedValue
@Column(name = "days_id")
private Long id;
@Enumerated(EnumType.STRING)
private DaysType days;
@OneToMany(mappedBy = "days")
private List<DaysOfStore> daysOfStoreList = new ArrayList<>();
// ...
}
- @ManyToMany를 DaysOfStore 엔티티를 활용하여 @OneToMany, @ManyToOne 관계로 풀어주었다.
- 이로인해 겪게될 문제점과 해결 방법은 다음 포스팅에서.
댓글남기기