N+1 문제를 해결하기 위해서 fetchJoin을 사용하여 해결해 주는 과정에서 yata와 연결된 yatamember 중 수락된 yatamember를 계산해서 보여줄 필요가 있었는데 수가 비정상적으로 많게 나오는 문제가 발생하였다.

 

 

@OneToMany의 경우 카테시안 곱 문제나
여러개의 fetchJoin을 사용할 경우 발생하는 
MultipleBagFetchException을 고려해줘야한다.

 

 

카테시안 곱
발생가능한 모든 경우의 수의 행이 조회되는 것
즉, N 개의 행을 갖진 테이블과 M개의 행을 가진 테이블의 카타시안 곱은 N * M이 되는 것

 

 

 

한번의 쿼리로 yataMember를 조회할 때 관련되어 있는 yata까지 한번에 조회할 수 있다.

fetchType이 Lazy로 설정되어있어도 fetchJoin이 우선순위를 가지기 때문에 한번의 조회로 연관된 엔티티를 같이 조회한다.

 

다대일 관계에서는 fetchJoin을 쓰면 N+1 문제를 해결할 수 있다.

하지만 OneToMany 관계에서는 데이터가 카테시안 곱만큼 늘어나서 조회된다는 것이다.

 

그림에서 보이는 것 처럼 Yata에 YataMember가 2명 존재하는 경우 fetchJoin으로 Yata와 YataMember를 조회하는 경우 Yata가 YataMember의 수만큼 곱해져 Yata가 2개로 조회되는 문제가 발생한다.

 

이 문제를 해결하기 위해서는 자료의 중복을 제거하는 두가지 방법을 일반적으로 사용한다.

1.컬렉션 Set을 사용(중복허용X)

2.distinct를 사용

 

따라서 yata porject에서는 yataMapper 클래스에서 yataMemeber를 얻어올 때 Distinct를 사용해 중복을 제거해줌으로써, 문제를 해결하였다.

 

 

💡yataMapper 클래스 

default YataDto.Response yataToYataResponse(Yata yata) {
    if (yata == null) {
        return null;
    }

    YataDto.Response.ResponseBuilder response = YataDto.Response.builder();

    if (yata.getYataId() != null) {
        response.yataId(yata.getYataId());
    }
    if (yata.getYataMembers() == null) response.reservedMemberNum(0);
    else response.reservedMemberNum(yata.getYataMembers().stream().distinct().mapToInt(YataMember::getBoardingPersonCount).sum()); //중복제거
    .
    .
    .
    return response.build();
}

default List<YataDto.AcceptedResponse> yataToMyYatas(List<Yata> yatas , String email){
    if (yatas == null) {
        return null;
    }
    return yatas.stream().map(yata -> {
        YataDto.AcceptedResponse response = new YataDto.AcceptedResponse();
        response.setYataResponse(yataToYataResponse(yata));
        response.getYataResponse().setYataMembers(null);
        YataMember yataMember = yata.getYataMembers()
                .stream().distinct() //중복제거
                .filter(yataMember1 ->
                        yataMember1.getMember().getEmail().equals(email)).findFirst().orElse(null);
        response.setYataMemberId(yataMember != null ? yataMember.getYataMemberId() : null);
        .
        .
        .
        return response;
    }).collect(Collectors.toList());
}

 

 

 

https://velog.io/@mohai2618/JPA%EC%97%90%EC%84%9C-fetchJoin%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-%EC%A3%BC%EC%9D%98%ED%95%B4%EC%95%BC%ED%95%A0-%EC%A0%90

'Spring' 카테고리의 다른 글

[프로젝트]토스페이 연동하기 2  (3) 2023.06.12
토스페이 API 연동  (0) 2023.06.09
[프로젝트] Spring batch의 적용  (6) 2023.06.06
Spring batch  (0) 2023.06.05
ehcache의 속성  (2) 2023.06.02
복사했습니다!