[Querydsl] ElementCollection Projection 할 때 에러

Cho-D-YoungRae·2022년 8월 24일
1
post-thumbnail

문제 상황

Querydsl을 사용해서 Entity를 DTO에 Projections.constructor를 사용해 projection 하던 중 org.hibernate.QueryException: not an entity 문제가 발생하였습니다.
문제가 되는 상황은 아래와 같습니다. 엔터티 구조에 대한 것 말고 이러한 메서드가 실행되었고, 어떤 에러가 발생했는지만 확인해주세요.

위와 같은 메서드가 실행되었고

Caused by: org.hibernate.QueryException: not an entity [select bookmark.id, recruiting.id, recruiting.title, recruiting.recruitingEndDate, recruiting.commentCount, recruiting.bookmarkCount, recruiting.recruitingMemberCount, project.name, lectureProject.lectureTimes
from com.dnd.niceteam.domain.bookmark.Bookmark bookmark
  inner join bookmark.member
  inner join bookmark.recruiting as recruiting
  inner join bookmark.recruiting.project as project
  left join com.dnd.niceteam.domain.project.LectureProject lectureProject with lectureProject = project
where bookmark.member = ?1]

위와 같은 에러메세지가 발생하였습니다.

문제 인식

위 메서드에서 DTO 로 변환하는 필드 중 lectureTimes는 ElementCollection 으로 구현되어 있어서 발생하는 문제였습니다.

ElementCollection 으로 사용되는 것은 별도의 테이블에 저장되는 것이므로 문제 상황에서와 같이 간단하게 되는 것이 아니었습니다.

문제 해결

문제를 해결하는 가장 핵심은 "쿼리를 꼭 한번에 날리려 하지 않아도 된다!" 였습니다. ElementCollection 도 1:N 과 같은데 이것은 DTO로 가져올 때는 BatchSize 등을 이용하기도 어렵기 때문에 쿼리 한번에 가져오기는 어려웠습니다. 그렇기 때문에 ElementCollection 을 제외하고 먼저 쿼리를 실행하고 그 후 ElementCollection 들을 가져와서 매핑해주는 방식으로 쿼리를 두번으로 나누었습니다.
그 이외에도 코드에서 Entity는 아니지만 Embedable 타입으로 사용되던 LectureTime 은 DTO 로 변환했고, Projections.constructor 를 사용하던 것을 DTO 생성자에 @QueryProject 를 사용하도록 변경되었습니다만 코드상에서 크게 중요하지 않기 때문에 쿼리가 두번으로 나뉘어 진 것을 중심으로 봐주시면 좋을 것 같습니다.

수정된 메서드

ElementCollection 쿼리

ElementCollection 쿼리 보충

ElementCollection 조회 쿼리에서 조회의 id (위 메서드에서는 project.id) 에 대한 ElementCollection 이 없으면 생성된 DTO(위 메서드의 경우 LectureTimeDto) 에서 조희의 id 만 들어가고 나머지 필드의 경우 null 이 들어가게 됩니다 (위의 경우 project.id는 들어가고 lecrtureTime.dayOfWeek, lectureTime.startTime은 null이 들어가게 됨). 그래서 이런 상황에 대한 방어 코드가 필요했습니다.

참고

참고는 했지만 적용은 안함

관련 문제들을 찾아보았는데, transform, groupBy 메서드를 사용하는 예제도 많이 있었습니다. 그러나 우선 제가 제대로 이해하고 있지 않은 메서드였고, 코드 자체가 보기 어려웠습니다. 그러던 중 참고했던 링크의 말씀을 보고 쿼리를 나누는 방법으로 진행하게 되었습니다.

0개의 댓글