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 관계로 풀어주었다.
- 이로인해 겪게될 문제점과 해결 방법은 다음 포스팅에서.
댓글남기기