HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📝
남득윤 학습 저장소
/
JPA
JPA
/
🧲
JPA 다양한 상황에서 dirty checking 에 따른delete, insert 의 쿼리
🧲

JPA 다양한 상황에서 dirty checking 에 따른delete, insert 의 쿼리

하나의 Profile 은 여러개의 Favorite Category 를 가질 수 있다.
favorite category 를 변경하고 싶다.
IT, HUMANITIES → HUMANITIES, SCIENCE
 
dirty checking 을 위해 favorite category 의 참조 혹은 내용을 바꾸는 방식으로 업데이트를 수행할 수 있다.
크게 세가지 방법이 있다.
  1. 기존 목록 중 업데이트 목록에 없다면 삭제 → 업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
  1. 전체 삭제 → 전체 추가
  1. 덮어쓰기
 
각각을
Case 1. Favorite Category 를 @Entity 로 구현한 경우 with orphan Removal true
Case 2. Favorite Category 를 @Entity 로 구현한 경우 with orphan Removal false
Case 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<>(); }
 
  1. 기존 목록 중 업데이트 목록에 없다면 삭제 → 업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
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;
 
  1. 전체 삭제 → 전체 추가
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;
 
  1. 덮어쓰기
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<>(); }
 
  1. 기존 목록 중 업데이트 목록에 없다면 삭제 → 업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
java.lang.AssertionError: Expecting actual: [HUMANITIES, POLITICS, SCIENCE] to contain exactly (and in same order): [HUMANITIES, SCIENCE] but some elements were not expected: [POLITICS]
삭제가 발생하지 않음
 
  1. 전체 삭제 → 전체 추가
  1. 덮어쓰기
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<>(); }
 
  1. 기존 목록 중 업데이트 목록에 없다면 삭제 → 업데이트 목록 중 기존 목록에 포함되지 않았으면 추가
  1. 전체 삭제 → 전체 추가
  1. 덮어쓰기
모든 경우에 대해 아래의 쿼리 발생
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 을 이용해 간단하게 구현하자!