(Project)페이징 처리 시 발생한 문제
페이징 처리 시 발생한 문제
문제 상항
- 현재 Issue및 Page 상태
1
2
3
4
- Issue Entity는 Member와 Milestone Entity에 대해 @ManyToOne 관계를 맺고 있다.
- Milestone 의 경우 Issue에 등록 되어 있을 수도 있고 없을 수도 있다.
- Issue 를 OPEN, COLSE 상태 별로 전체 조회하는 로직이 필요하다.
- 첫 시작 Page 는 1로 해야하고, Size는 25이다.
Issue-tracker 프로젝트
를 진행하면서Issue 조회
에 대해 페이징 처리를 하던 중 문제가 두 가지 발생하였다.- 첫번째 문제로는
Issue에 Milestone 이 없는 경우
전체 조회를 진행하여도 리스트에 나타나지 않는 문제가 있었다. - 두번째 문제로는
query specified join fetching, but the owner of the fetched association was not present in the select list
라는 문구의 예외가 발생하였다.
문제 코드
1
2
@Query(value = "select i from Issue i join fetch i.member m join fetch i.milestone mi where i.status = :status")
Page<Issue> findIssues(Pageable pageable, @Param("status") IssueStatus status);
- 위 코드가
OPEN
,CLOSE
상태 별 Issue 를 전체 조회 하는 코드이다. - 딱 봤을때 문제가 없는 코드라고 생각하였는데, 두 가지 문제가 발생하여 많이 당황했다.
첫번째 문제
- 상태별 Issue를 조회 할 때 해당 Issue에
Milestone 이 등록되어 있지 않은 경우
Issue를 조회 해도 리스트에 나타나지 않는 문제가 있다.
문제 원인
- 그냥
join fetch
를 했을때 어떤 join 이 발생하는지 알고 있다면 쉽게 해결할 수 있는 문제다. 그냥join fetch
를 하면inner join
이 발생하게 되는데inner join
의 경우 join 대상이null
일 경우 데이터를 가져오지 않기 때문에 Issue를 조회할때 Milestone이 없을 경우 리스트에 나타나지 않는것이였다.
해결 방법
left join fetch
를 하게되면left join
이 발생하기 때문에 join 대상인 Milestone 에 값이 없어도 Issue를 가져오기 때문에 이 방법으로 해결 할 수 있었다.
해결 코드
1
2
@Query(value = "select i from Issue i left join fetch i.member m left join fetch i.milestone mi where i.status = :status")
Page<Issue> findIssues(Pageable pageable, @Param("status") IssueStatus status);
두번째 문제
- 이번에는
query specified join fetching, but the owner of the fetched association was not present in the select list
라는 문구의 예외가 발생하면서 실행조차 되지 않았다. - 우선
N+1
문제를 해결하기 위해@ManyToOne
관계를 가진 Entity를join fetch
시켜주었다. 그리고 Issue의 상태별 조회를 위해 where 문을 사용하였다. - @ManyToOne 관계에 join fetch 를 하는것은 데이터의 뻥튀기가 없기 때문에 이부분은 문제가 아니라 생각했다.
- join fetch 시 where문을 사용하는것은 join 대상에 where 문을 사용하는 것이 아니라면 문제가 없기 때문에 이부분도 문제가 아닐것이라 생각했다.
해결 방법
- 해결 방법이 도저히 생각이 안나 검색을 하기 시작했다. 다행히
stackoverflow
나와 같은 문제를 겪고 있는 사람의 글을 보게되었다. - 해결 방법으로는
count query
를 따로 작성해 주면 된다는 것이였다. 이부분에서 오잉? 하고 의문이 들었다. 알고 있기로는 페이징 처리를 하면 임의로count query
를 생성해서 날려주는 것으로 알고 있었기 때문이다. - 검색을 통해 문제를 해결하긴 했지만, 여전히 왜 이런 문제가 발생하는지 너무 궁금하여 인프런을 통해 영한님께 질문을 드렸다.
문제 원인
- 인프런에 달려있던 영한님의 답변으로 왜 이런 문제가 발생하는지 알 수 있었다.
JPA가 임의로 날려주는 쿼리
의 경우JPA가 많이 똑똑하지 못해
조회 결과가 바뀌어 버리기 때문에 정상적인 count의 값을 가져올 수 없어 발생 하는 문제라고 하셨다.fetch join
이나where 문
을 함께 사용하는 복잡한 쿼리의 경우count query
를 꼭 분리해서 사용해야한다는 답을 얻었다.
해결 코드
1
2
3
@Query(value = "select i from Issue i left join fetch i.member m left join fetch i.milestone mi where i.status = :status",
countQuery = "select count(i) from Issue i left join i.member left join i.milestone where i.status = :status")
Page<Issue> findIssues(Pageable pageable, @Param("status") IssueStatus status);
REFERENCE
This post is licensed under CC BY 4.0 by the author.