feat: api wrapping
This commit is contained in:
@@ -5,8 +5,12 @@ import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.repository.ZooRepository;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -56,10 +60,23 @@ public class ZooCoreService implements BaseCoreService<ZooDto.Detail, Long, ZooD
|
||||
@Override
|
||||
public Page<ZooDto.Detail> search(ZooDto.SearchReq searchReq) {
|
||||
Page<ZooEntity> zooEntities = zooRepository.listZoo(searchReq);
|
||||
return zooEntities.map(this::toDetailDto);
|
||||
|
||||
// N+1 문제 해결: 한 번의 쿼리로 모든 Zoo의 animal count 조회
|
||||
List<Long> zooIds =
|
||||
zooEntities.getContent().stream().map(ZooEntity::getUid).collect(Collectors.toList());
|
||||
|
||||
Map<Long, Long> animalCountMap = zooRepository.countActiveAnimalsByZooIds(zooIds);
|
||||
|
||||
// DTO 변환
|
||||
List<ZooDto.Detail> details =
|
||||
zooEntities.getContent().stream()
|
||||
.map(zoo -> toDetailDtoWithCountMap(zoo, animalCountMap))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageImpl<>(details, zooEntities.getPageable(), zooEntities.getTotalElements());
|
||||
}
|
||||
|
||||
// Entity -> Detail DTO 변환 (동물 개수 포함)
|
||||
// Entity -> Detail DTO 변환 (동물 개수 포함) - 단건 조회용
|
||||
private ZooDto.Detail toDetailDto(ZooEntity zoo) {
|
||||
Long activeAnimalCount = zooRepository.countActiveAnimals(zoo.getUid());
|
||||
return new ZooDto.Detail(
|
||||
@@ -72,4 +89,18 @@ public class ZooCoreService implements BaseCoreService<ZooDto.Detail, Long, ZooD
|
||||
zoo.getModifiedDate(),
|
||||
activeAnimalCount);
|
||||
}
|
||||
|
||||
// Entity -> Detail DTO 변환 (동물 개수 포함) - 배치 조회용 (N+1 해결)
|
||||
private ZooDto.Detail toDetailDtoWithCountMap(ZooEntity zoo, Map<Long, Long> countMap) {
|
||||
Long activeAnimalCount = countMap.getOrDefault(zoo.getUid(), 0L);
|
||||
return new ZooDto.Detail(
|
||||
zoo.getUid(),
|
||||
zoo.getUuid().toString(),
|
||||
zoo.getName(),
|
||||
zoo.getLocation(),
|
||||
zoo.getDescription(),
|
||||
zoo.getCreatedDate(),
|
||||
zoo.getModifiedDate(),
|
||||
activeAnimalCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.postgres.repository;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
@@ -14,4 +16,12 @@ public interface ZooRepositoryCustom {
|
||||
Optional<ZooEntity> getZooByUid(Long uid);
|
||||
|
||||
Long countActiveAnimals(Long zooId);
|
||||
|
||||
/**
|
||||
* 여러 Zoo의 활성 동물 수를 한 번에 조회 (N+1 문제 해결)
|
||||
*
|
||||
* @param zooIds Zoo ID 목록
|
||||
* @return Map<Zoo ID, 활성 동물 수>
|
||||
*/
|
||||
Map<Long, Long> countActiveAnimalsByZooIds(List<Long> zooIds);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
import com.querydsl.jpa.impl.JPAQuery;
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -75,6 +77,39 @@ public class ZooRepositoryImpl implements ZooRepositoryCustom {
|
||||
return count != null ? count : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, Long> countActiveAnimalsByZooIds(List<Long> zooIds) {
|
||||
if (zooIds == null || zooIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
// QueryDSL group by로 한 번에 조회
|
||||
List<com.querydsl.core.Tuple> results =
|
||||
queryFactory
|
||||
.select(qAnimal.zoo.uid, qAnimal.count())
|
||||
.from(qAnimal)
|
||||
.where(qAnimal.zoo.uid.in(zooIds), qAnimal.isDeleted.eq(false))
|
||||
.groupBy(qAnimal.zoo.uid)
|
||||
.fetch();
|
||||
|
||||
// Map으로 변환
|
||||
Map<Long, Long> countMap = new HashMap<>();
|
||||
for (com.querydsl.core.Tuple tuple : results) {
|
||||
Long zooId = tuple.get(qAnimal.zoo.uid);
|
||||
Long count = tuple.get(qAnimal.count());
|
||||
if (zooId != null && count != null) {
|
||||
countMap.put(zooId, count);
|
||||
}
|
||||
}
|
||||
|
||||
// 조회되지 않은 Zoo는 0으로 설정
|
||||
for (Long zooId : zooIds) {
|
||||
countMap.putIfAbsent(zooId, 0L);
|
||||
}
|
||||
|
||||
return countMap;
|
||||
}
|
||||
|
||||
private BooleanExpression nameContains(String name) {
|
||||
return name != null && !name.isEmpty() ? qZoo.name.contains(name) : null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user