페이징 처리시 jpaRepository 가 제공하는 findAll에 Pageable 객체를 넣어주면 알아서 페이징처리하지만 이런 경우 필터링하여 원하는 결과를 얻을 수가 없기 때문에 필터링을 먼저한 후 수동으로 페이징처리를 해줘야 한다.
@Transactional(readOnly = true)
public StorePageResponseDto findAll(int page, int pageSize, String name, StoreCategory category) {
List<Store> storeList = storeRepository.findAllByNameAndCategory(name, category);
List<Store> filteredStores = storeList.stream()
.filter(store -> !store.getStatus().equals(StoreStatus.SHUTDOWN))
.toList(); //페이징처리하기전에 폐업한 가게 필터링을 먼저해야함
int pageNum = (page > 0) ? page - 1 : 0; //사람한테는 1이 시작이지만 jpa가 제공하는 page는 시작 번호는 0번부터임
Pageable pageable = PageRequest.of(pageNum, pageSize, Sort.by(Sort.Direction.ASC, "minimumPrice"));
int totalElements = filteredStores.size(); //전부 몇개인지
int start = (int) pageable.getOffset(); //시작 번호
int end = Math.min((start + pageable.getPageSize()), totalElements); //끝 번호
List<Store> pageContent = filteredStores.subList(start, end);
Page<Store> storePage = new PageImpl<>(pageContent, pageable, totalElements); //필터링된 가게를 여기서 페이징 처리
int currentPage = storePage.getNumber() + 1; //현재 페이지 번호 사람은 1이 시작이니까 + 1
int totalPages = storePage.getTotalPages(); //총 페이지 번호
List<StoreResponseDto> stores = storePage.stream()
.map(store -> new StoreResponseDto(
store.getId(),
store.getTelephoneNumber(),
store.getAddress(),
store.getContents(),
store.getName(),
store.getOpenTime(),
store.getCloseTime(),
store.getMinimumPrice(),
store.getCategory(),
store.getStatus()
)).toList();
return new StorePageResponseDto(stores, currentPage, totalPages, totalElements);
}
int end = Math.min((start + pageable.getPageSize()), totalElements);
이 부분의 코드는 현재 페이지의 끝 인덱스를 계산하기 위해 사용됩니다. 구체적으로 설명하면:
- start는 현재 페이지의 시작 인덱스를 나타냅니다. 이는 pageable.getOffset()를 통해 구해집니다.
- pageable.getPageSize()는 페이지당 요소의 수를 나타냅니다.
start + pageable.getPageSize()는 현재 페이지의 마지막 요소가 포함된 인덱스를 계산합니다. 예를 들어, 페이지당 요소가 10개이고 시작 인덱스가 20이라면, 마지막 요소는 29번째 인덱스가 됩니다.
하지만 이 계산된 끝 인덱스가 전체 요소 수(totalElements)를 넘지 않도록 하기 위해 Math.min() 함수를 사용합니다. 이 함수는 두 값을 비교하여 더 작은 값을 반환합니다. 따라서 계산된 끝 인덱스가 전체 요소 수를 넘을 경우 전체 요소 수가 끝 인덱스로 설정됩니다.
예시로 설명하자면:
- 전체 요소 수(totalElements)가 25개이고, 페이지당 10개씩, 3번째 페이지를 요청했다고 가정합니다.
- start = 20 (2 * 10)
- end = Math.min(20 + 10, 25) -> end = Math.min(30, 25) -> end = 25
이렇게 해서 끝 인덱스가 25로 설정되며, 현재 페이지에 포함될 요소의 범위가 20부터 24까지로 제한됩니다.
List<Store> pageContent = filteredStores.subList(start, end);
이 코드는 필터링된 전체 리스트에서 특정 범위의 요소들을 추출하기 위해 사용됩니다. 구체적으로 설명하면:
- filteredStores: 이 리스트는 앞서 필터링된 전체 Store 객체의 목록입니다.
- start: 이 변수는 현재 페이지의 시작 인덱스를 나타냅니다.
- end: 이 변수는 현재 페이지의 끝 인덱스를 나타냅니다.
subList(start, end) 메서드는 filteredStores 리스트에서 start 인덱스부터 end 인덱스까지의 부분 리스트를 반환합니다.
인덱스는 0부터 시작하며, end 인덱스는 포함되지 않습니다.
따라서, start 인덱스부터 end - 1 인덱스까지의 요소들이 반환됩니다.
예를 들어, filteredStores 리스트가 다음과 같다고 가정해봅시다:
List<Store> filteredStores = Arrays.asList(store1, store2, store3, store4, store5);
만약 start = 1이고 end = 4라면, subList(start, end)는 다음과 같은 리스트를 반환합니다:
List<Store> pageContent = filteredStores.subList(1, 4); // [store2, store3, store4]
즉, store2, store3, store4 객체들이 포함된 부분 리스트를 반환합니다.
이렇게 하면 현재 페이지에 해당하는 요소들만 추출할 수 있게 됩니다.
위에서 사용한 레포지터리 메서드
public interface StoreRepository extends JpaRepository<Store, Long> {
//카테고리가 같은지 찾음
//이름이 비어있지 않으며 이름이 like name 조건에 해당하면 우선순위 0 아니면 1로 이름 오름차순정렬
@Query("SELECT s FROM Store s WHERE s.category = :category " +
"ORDER BY CASE WHEN :name IS NOT NULL AND s.name LIKE %:name% THEN 0 ELSE 1 END, s.name ASC")
List<Store> findAllByNameAndCategory(@Param("name") String name, @Param("category") StoreCategory category);
}
가게의 이름을 우선으로 비슷한걸 찾고 없으면 카테고리 찾는 쿼리문을 사용했다.
'spring' 카테고리의 다른 글
| swagger 사용시 유용함. @Tag, @Operation (0) | 2025.03.06 |
|---|---|
| 테스트 코드 (0) | 2025.03.06 |
| JPA 사용시 변경사항이 있을때 save() 가 아닌 @Transactional 사용해도 저장됨 (0) | 2025.02.26 |
| 관계형DB일때 N:M 연관관계 @ManyToMany (0) | 2025.02.24 |
| @Mock (0) | 2025.02.24 |