진행 중인 프로젝트에는 폴더마다 여러개의 태그가 존재한다.
또한 태그에는 대분류, 소분류가 존재하여 RootTag
와 Tag
로 나뉜다.
필자는 태그로 폴더 검색 기능을 구현하던 중, 다음과 같은 문제를 겪었다.
@Query(value = "SELECT DISTINCT f FROM Folder f JOIN FETCH f.user " +
"JOIN FETCH f.folderTags ft JOIN ft.tag t " +
"ON t.name = :tag WHERE f.isPrivate = false",
countQuery = "SELECT count(f) FROM Folder f")
Page<Folder> findByTag(TagCategory tag, Pageable pageable);
위와 같은 쿼리를 사용하면 검색을 위해 사용된 태그만 가지고 있는 객체들이 반환된다.
당연하게도 on 절에 t.name = :tag
를 사용했기 때문이다.
원하는 것은 원래 가지고 있는 태그들을 온전히 가진 폴더 객체들을 가져오는 것이었고, 따라서 서브쿼리를 사용하게 되었다.
@Query(value = "SELECT DISTINCT f FROM Folder f " +
"JOIN FETCH f.user JOIN FETCH f.folderTags ft " +
"WHERE f.isPrivate = false AND f.id IN " +
"(SELECT subf.id FROM Folder subf JOIN subf.folderTags subft " +
"JOIN subft.tag subt ON subt.name = :tag)",
countQuery = "SELECT count(f) FROM Folder f")
Page<Folder> findByTag(TagCategory tag, Pageable pageable);
서브쿼리를 사용하여 해당 태그를 가지고 있는 폴더의 id를 다중열로 select한 뒤, 해당 id를 가지고 있는 폴더들을 select하는 로직으로 수정하였다.
이제 FolderTag를 fetch join하면 폴더가 가지고있는 태그들이 잘 받아와진다.
하지만, 서브쿼리를 사용한 방법은 성능이 좋지 않다.
따라서 비정규화를 이용한 방법 등을 고민해봐야할 것 같다.