(Project)페이징 처리 시 발생한 문제
(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.