기존 이름은 'Join시 n+1 문제 Map으로 해결' 이었으나 생각해보니 n+1 문제가 아니라 그냥 최적화 문제이므로 수정했다.
회사에서 사용중인 DB는 외래키가 존재하지 않고 양이 워낙 많아(테이블만 280개..) JPA를 활용한 batch_fetch_size의 기능을 쓰기 어렵다.
따라서 이를 자료구조로 해결하고자 방향을 잡고 생각해보니 Map을 활용하면 꽤나 효율적으로 작업을 할 수 있을 것 같았다.
부모 spot 테이블과 자식 spotImg, spotTheme 테이블을 예로 들어 설명해보겠다.
일단 spot에서 필요한 데이터만 가지고 온다. (자세한 쿼리는 보안상 생략)
List<FirstSpotJoinDto> spotListByCondition = spotRepository.getSpotListByCondition(spotId, seId);
자식 데이터를 WHERE IN 절을 통해 가지고 온다. (batch_fetch_size의 동작 방식에서 아이디어를 얻었다.)
WHERE IN 절을 사용하기 위해 spotListByCondition 에서 spotId만 추출해 List로 만든다.
List<Integer> spotIdList = spotListByCondition.stream().map(FirstSpotJoinDto::getSpotId).toList();
List<SpotImg> spotImgList = spotImgRepository.findBySpotIdInAndState(spotIdList, "Y");
List<SpotThemeRel> spotThemeList = spotThemeRelRepository.findBySpotIdInAndStateAndSubState(spotIdList, "Y", "Y");
이 데이터들을 spotId를 키로 갖는 MultiValueMap에 등록한다.
MultiValueMap을 쓰는 이유는 단순하게 이들이 1:N 관계이기 때문이다. (key : 1 , value : N)
시간복잡도 O(N)으로 문제를 해결할 수 있게 된다.
join시 발생하는 중복되는 부모의 데이터도 받을 필요가 없다.
MultiValueMap<Integer, List<String>> imgMap = new LinkedMultiValueMap<>();
for (SpotImg spotImg : spotImgList) {
imgMap.add(spotImg.getSpotId(), Arrays.asList(spotImg.getUrl(), String.valueOf(spotImg.getOnum())));
}
MultiValueMap<Integer, Integer> themeMap = new LinkedMultiValueMap<>();
for (SpotThemeRel theme : spotThemeList) {
themeMap.add(theme.getSpotId(), theme.getThemeId());
}
이제 처음에 가져왔던 spotListByCondition 을 루프로 돌면서 비지니스 로직을 처리하면 된다.
자식의 데이터가 필요하다면 그저 Map에 spotId를 넣어서 O(1)의 시간복잡도로 데이터를 얻을 수 있다.
'JAVA > Spring Boot' 카테고리의 다른 글
Compile(API), Implement 차이점 (0) | 2023.05.14 |
---|---|
Annotation) 커스텀 어노테이션으로 Enum 유효성 검사하기 (0) | 2023.03.13 |
JPA) [수정]@DynamicUpdate, @DynamicInsert (0) | 2023.02.07 |
[Validation] 누락된 값 전역 처리, 클라이언트에 상세히 표시 (0) | 2023.01.25 |
Spring Boot, Gradle 멀티 모듈 프로젝트 실전 예제 (0) | 2022.12.28 |