JPA 페이징 이슈

최성욱·2025년 3월 4일
0

Spring JPA Data에는 Pageable 인터페이스를 제공하게 되며, 페이징을 쉽게 할 수 있다.
하지만, JPA에도 페이징이 안되는 이슈가 있다는 것을 발견하였다

다음은 Gathering Entity이다

public class Gathering {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String content;
    private LocalDateTime registerDate;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User createBy;

    private int count
}

다음은 Category Entity이다

public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

}

즉, Gathering과 Category는 @ManyToOne으로 연관관계가 잡혀있다. 그 뿐만 아니라, Gathering Table에 외래키(user_id)가 존재한다는 것을 확인할 수 있다

나의 목적은 카테고리별로 count수가 높은 순서대로 orderby를 한후에 8개(pageSize)를 가지고 오는 것이다. 따라서 쿼리를 다음과 같이 처음에 구성하였다(count쿼리는 길어서 생략하였다)

@Query(value = "select id,title,content,registerDate,category,createdBy,url,count from ( " +
            "  select g.id as id, g.title as title, g.content as content, g.register_date as registerDate, ca.name as category, " +
            "         cr.username as createdBy, im.url as url, g.count as count, " +
            "         row_number() over (partition by ca.id order by g.count desc) as rownum " +
            "  from gathering g " +
            "  left join category ca on g.category_id = ca.id " +
            "  left join user cr on g.user_id = cr.id " +
            "  left join image im on g.image_id = im.id " +
            "  where g.title like concat('%', :title, '%') " +
            ") as subquery"
            ,nativeQuery = true)
    Page<EntireGatheringsQuery> gatherings(Pageable pageable;

다음은 Pageable을 구현한 것이다

Pageable = PageReuest.of(pageNum,8)

위 논리대로라면, pageNum이 예를들어 3이였을때 3번쨰 페이지를 8개 가지고 와야한다(Pageable은 page번호가 0부터 시작)
하지만, 내 예상과 다르다는 것을 확인할 수 있었다
카테고리별로 paging이 처리된 것이 아니라 전체 row수 기준으로 처리된 것이다
즉, 카테고리별로 8개씩 가지고 와야하는데 page의 elementSize보니 8이 나온것이다

그래서, JPA 문서를 찾아보니 JPA 페이징은 전체 row수 기준으로 paging을 처리한 것이다.
그에 따라, 각 카테고리별로 8개씩 가져온 후에, Application Level에서 처리하기로 하였다

다음은 수정한 쿼리이다(Pageable parameter을 빼고 List를 반환하도록 하였다)

@Query(value = "select id,title,content,registerDate,category,createdBy,url,count from ( " +
            "  select g.id as id, g.title as title, g.content as content, g.register_date as registerDate, ca.name as category, " +
            "         cr.username as createdBy, im.url as url, g.count as count, " +
            "         row_number() over (partition by ca.id order by g.id asc) as rownum " +
            "  from gathering g " +
            "  left join category ca on g.category_id = ca.id " +
            "  left join user cr on g.user_id = cr.id " +
            "  left join image im on g.image_id = im.id " +
            "  where g.title like concat('%', :title, '%') " +
            ") as subquery " +
            "where rownum between 1 and 9", nativeQuery = true)
    List<EntireGatheringsQuery> gatherings(@Param("title") String title);

다음은 카테고리별로 Gathering이 존재하는 List를 Map으로 반환하는 코드이다(이하 생략)

List<EntireGatheringsQuery> entireGatheringsQueries = gatheringRepository.gatherings(title);
            List<GatheringsQuery> gatherings = toGatheringQueriesList(entireGatheringsQueries);
            List<TotalGatheringsElement> totalGatheringsElements = toGatheringsResponseList(gatherings);
            Map<String, CategoryTotalGatherings> map = categorizeByCategory(totalGatheringsElements);
profile
성장을 지향하는 개발자

0개의 댓글