하나의 Profile 은 여러개의 Favorite Category 를 가질 수 있다.
favorite category 를 변경하고 싶다.
IT, HUMANITIES
→ HUMANITIES, SCIENCE
dirty checking 을 위해 favorite category 의 참조 혹은 내용을 바꾸는 방식으로 업데이트를 수행할 수 있다.
크게 세가지 방법이 있다.
기존 목록 중 업데이트 목록에 없다면 삭제
→업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
전체 삭제
→전체 추가
덮어쓰기
각각을
Case 1. Favorite Category 를
@Entity
로 구현한 경우 with orphan Removal trueCase 2. Favorite Category 를
@Entity
로 구현한 경우 with orphan Removal falseCase 3. Favorite Category 를
@ElementCollection
으로 구현한 경우에 대해 적용해보고 쿼리를 확인해 보자
Case 1. Favorite Category 를 @Entity 로 구현한 경우 with orphan Removal true
@Entity public class Profile extends BaseIdEntity{ @OneToMany(mappedBy = "profile", cascade = ALL, orphanRemoval = true) private List<FavoriteCategory> favoriteCategories = new ArrayList<>(); }
기존 목록 중 업데이트 목록에 없다면 삭제
→업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
public void updateFavoriteCategories(final List<Category> categories) { /* 기존 목록 중 업데이트 목록에 없다면 삭제 */ favoriteCategories.removeIf(fc -> !categories.contains(fc.getCategory())); /* 업데이트 목록 중 기존 목록에 포함되지 않았으면 추가 */ categories.stream() .filter(c -> !favoriteCategories.stream() .map(FavoriteCategory::getCategory) .collect(toList()).contains(c)) .forEach(c -> favoriteCategories.add(new FavoriteCategory(this, c))); }
insert into favorite_category (id, category, profile_id) values (default, 'SCIENCE', 1); delete from favorite_category where id=2;
전체 삭제
→전체 추가
public void updateFavoriteCategories(final List<Category> categories) { /* 전체 삭제 */ favoriteCategories.clear(); /* 전체 추가 */ categories.forEach(c -> favoriteCategories.add(new FavoriteCategory(this, c))); }
insert into favorite_category (id, category, profile_id) values (default, 'SCIENCE', 1); insert into favorite_category (id, category, profile_id) values (default, 'SCIENCE', 1); delete from favorite_category where id=1; delete from favorite_category where id=2;
덮어쓰기
org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
Case 2. Favorite Category 를 @Entity 로 구현한 경우 with orphan Removal false
@Entity public class Profile extends BaseIdEntity{ @OneToMany(mappedBy = "profile", cascade = ALL, orphanRemoval = false) private List<FavoriteCategory> favoriteCategories = new ArrayList<>(); }
기존 목록 중 업데이트 목록에 없다면 삭제
→업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
java.lang.AssertionError: Expecting actual: [HUMANITIES, POLITICS, SCIENCE] to contain exactly (and in same order): [HUMANITIES, SCIENCE] but some elements were not expected: [POLITICS]
삭제가 발생하지 않음
전체 삭제
→전체 추가
덮어쓰기
java.lang.AssertionError: Expecting actual: [HUMANITIES, POLITICS, HUMANITIES, SCIENCE] to contain exactly (and in same order): [HUMANITIES, SCIENCE] but some elements were not expected: [POLITICS, HUMANITIES]
삭제가 발생하지 않음
orphan removal
을 끄면 delete 쿼리가 발생하지 않아 검증 에러가 발생한다.Case 3 . Favorite Category 를 @ElementCollection 으로 구현
@Entity public class Profile extends BaseIdEntity{ @ElementCollection @Enumerated(STRING) private List<Category> favoriteCategories = new ArrayList<>(); }
기존 목록 중 업데이트 목록에 없다면 삭제
→업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
전체 삭제
→전체 추가
덮어쓰기
모든 경우에 대해 아래의 쿼리 발생
delete from favorite_category where profile_id=1; insert into favorite_category (profile_id, category) values (1, 'HUMANITIES'); insert into favorite_category (profile_id, category) values (1, 'SCIENCE');
모든 케이스를 탐색해본 결과
Case1 + 1.기존 목록 중 업데이트 목록에 없다면 삭제 → 업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
혹은
Case3 + 3.덮어쓰기
가 가장 좋은 선택지 처럼 보인다. Case1+1
은 기존 선호 카테고리와 변경 선호 카테고리가 많이 겹치는 경우 발생하는 쿼리의 수가 적다.Case3+3
은 변경 선호 카테고리의 숫자가 적은 경우 발생하는 쿼리의 수가 적다. dirty checking 만을 이용해 선호카테고리 목록의 업데이트를 수행할 계획이므로 @ElementCollection 을 이용해 간단하게 구현하자!