UMC 4주차 시니어 미션 진행합니다.
성능을 고려한 연관관계 매핑 & 최적화 적용
@OneToMany 컬렉션을 조회할 때 List<MemberPrefer>를 Set<MemberPrefer>로 변경 후 차이점 분석orphanRemoval = true가 필요한 곳 확인 후 적용List vs Set 비교해보기
List - 순서 보장
일반적으로 순서가 보장되어, 클라이언트에 저장 순서대로 그대로 응답할 때에 편리하고 직관적임
게시글과 같이 최신순으로 저장하고 조회하는 요구사항이 일반적일 경우 List를 사용
List 타입 컬렉션을 2개 이상 Fetch join할 경우, 예외가 발생함 :MultipleBagFetchException 발생
Featch join은 꼭 필요한 연관 객체를 1개만 사용하도록 하고, 나머지는 @BatchSize를 사용하여 지연 로딩 시에 N+1문제를 완화하거나, 전역으로 batch_fetch_size를 설정하는 방법이 있음
다음과 같이 해결 가능
@BatchSize(size = 100)
@OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
private List<UserCategory> userCategories;
Set - 순서 보장 안됨
orphanRemoval = true
@BatchSize(size = 100)
@OneToMany(mappedBy = "user", orphanRemoval = true)
private List<UserCategory> userCategories;
연관관계가 끊긴 엔티티에 대해서 REMOVE 작업을 진행하고, 전파할 지에 대한 옵션
true로 설정해두면, 연관관계가 끊어진 고아 객체를 자동으로 삭제, 부모 엔티티가 삭제되지 않아도, 연관관계만 끊으면 삭제되는 옵션이다.
다음과 같이 연관관계를 끊으면, usercategory가 DB에서 삭제된다.
// 연관관계 끊기
user.getUserCategories().remove(userCategory);
cascade = CascadeType.REMOVE와의 차이?
// 부모 삭제 -> 해당 user를 부모로 가지고 있는 userCategory가 모두 DELETE됨
userRepository.delete(user);
// 연관관계만 끊을 경우에는 DB에 여전히 남아있음
연관관계를 끊는것만으로는 삭제가 안됨, 부모 엔티티가 삭제될 때 자식도 함께 삭제된다.
orphanRemoval = true 사용해야 하는 곳
userCategory같은 중간 테이블에서 사용해야 한다. 카테고리와 user 간의 연관관계가 끊기면 삭제하도록 해야 한다. PERSIST 사용 시 자동 저장되도록 하기 때문에 일반적으로 둘 다 사용한다.
@OneToMany(mappedBy = "user",
cascade = CascadeType.PERSIST,
orphanRemoval = true)
private List<UserCategories> userCategories = new ArrayList<>();
중간 테이블의 삭제 흐름
유저 조회 → 카테고리 조회 → 선호 카테고리 삭제
SELECT * FROM user WHERE id = 1;
SELECT * FROM category WHERE id = 5;
// 삭제 호출 시, 지연로딩이 된다.
SELECT * FROM user_category WHERE user_id = 1;
여기서 만약에 BatchSize를 안쓰면, user가 100명이라고 할 때 각 유저의 유저 카테고리를 조회하면..
BatchSize 사용 안할경우
-- 1번: User 조회
SELECT * FROM user; -- 100명
-- N번: 각 User의 UserCategory 조회
SELECT * FROM user_category WHERE user_id = 1; -- User 1
SELECT * FROM user_category WHERE user_id = 2; -- User 2
SELECT * FROM user_category WHERE user_id = 3; -- User 3
...
SELECT * FROM user_category WHERE user_id = 100; -- User 100
-- 총 101번의 쿼리 발생, N+1 문제..
user 조회를 하고, 각 유저의 연관 데이터도 조회를 하게 된다.
@BatchSize 를 사용한다면..?
batchsize는 n개씩 묶어서 조회를 할거다라고 명시를 해주는 어노테이션이다. 즉 batchsize가 0이라고 하면, 10명씩 묶어서 IN 쿼리를 사용해서 조회하게 된다. User를 접근한다고 할 때, 위의 쿼리처럼 하나하나씩 접근하지 않고 한번에 쿼리를 날리게 된다.
BatchSize사용할 경우
User 1-10 → SELECT ... WHERE user_id IN (1,2,3,...,10)
User 11-20 → SELECT ... WHERE user_id IN (11,12,...,20)