diff --git a/api/src/main/java/vook/server/api/app/TermSearchRepository.java b/api/src/main/java/vook/server/api/app/TermSearchRepository.java new file mode 100644 index 0000000..3ee1bbb --- /dev/null +++ b/api/src/main/java/vook/server/api/app/TermSearchRepository.java @@ -0,0 +1,49 @@ +package vook.server.api.app; + +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; +import vook.server.api.helper.QuerydslHelper; +import vook.server.api.model.QTerm; +import vook.server.api.model.Term; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class TermSearchRepository { + + private final JPAQueryFactory queryFactory; + + public List search(String glossaryUid, Pageable pageable) { + QTerm term = QTerm.term1; + + // fetch join과 pagination을 같이 하면, + // HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory + // 에러가 발생, 이를 피하기 위해 pagination이 포함된 쿼리를 서브 쿼리로 하여 수행 + JPAQuery termIdQuery = queryFactory + .select(term.id) + .from(term) + .where(term.glossary.uid.eq(glossaryUid)) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()); + + QuerydslHelper + .toOrderSpecifiers(term, pageable) + .forEach(termIdQuery::orderBy); + + JPAQuery dataQuery = queryFactory + .selectFrom(term) + .leftJoin(term.synonyms).fetchJoin() + .where(term.id.in(termIdQuery)); + + // In 절에 들어간 입력순대로 출력되지 않음으로 정렬 조건을 다시 한번 설정 + QuerydslHelper + .toOrderSpecifiers(term, pageable) + .forEach(dataQuery::orderBy); + + return dataQuery.fetch(); + } +} diff --git a/api/src/main/java/vook/server/api/app/TermService.java b/api/src/main/java/vook/server/api/app/TermService.java index d656d61..2e99799 100644 --- a/api/src/main/java/vook/server/api/app/TermService.java +++ b/api/src/main/java/vook/server/api/app/TermService.java @@ -1,10 +1,10 @@ package vook.server.api.app; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import vook.server.api.model.Glossary; import vook.server.api.model.Term; -import vook.server.api.model.TermRepository; import java.util.List; @@ -12,9 +12,9 @@ @RequiredArgsConstructor public class TermService { - private final TermRepository repository; + private final TermSearchRepository searchRepository; - public List findAllBy(Glossary glossary) { - return repository.findAllByGlossary(glossary); + public List findAllBy(Glossary glossary, Pageable pageable) { + return searchRepository.search(glossary.getUid(), pageable); } } diff --git a/api/src/main/java/vook/server/api/model/TermRepository.java b/api/src/main/java/vook/server/api/model/TermRepository.java index 326d8a6..ca73f73 100644 --- a/api/src/main/java/vook/server/api/model/TermRepository.java +++ b/api/src/main/java/vook/server/api/model/TermRepository.java @@ -2,8 +2,5 @@ import org.springframework.data.jpa.repository.JpaRepository; -import java.util.List; - public interface TermRepository extends JpaRepository { - List findAllByGlossary(Glossary glossary); } diff --git a/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryApi.java b/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryApi.java index fcc4764..289891e 100644 --- a/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryApi.java +++ b/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryApi.java @@ -6,6 +6,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.data.domain.Pageable; import vook.server.api.web.common.CommonApiResponse; import java.util.List; @@ -36,13 +38,16 @@ class RetrieveApiResponse extends CommonApiResponse> { description = "성공", content = @Content( mediaType = "application/json", - schema = @Schema(implementation = FindAllTermsApiResponse.class) + schema = @Schema(implementation = RetrieveTermsApiResponse.class) ) ), }) - CommonApiResponse> findAllTerms(String glossaryUid); + CommonApiResponse> retrieveTerms( + String glossaryUid, + @ParameterObject Pageable pageable + ); - class FindAllTermsApiResponse extends CommonApiResponse> { + class RetrieveTermsApiResponse extends CommonApiResponse> { } } diff --git a/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryRestController.java b/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryRestController.java index 59f9e32..82b7f20 100644 --- a/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryRestController.java +++ b/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryRestController.java @@ -1,6 +1,8 @@ package vook.server.api.web.routes.glossary; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,8 +27,11 @@ public CommonApiResponse> retrieve() { @Override @GetMapping("/{glossaryUid}/terms") - public CommonApiResponse> findAllTerms(@PathVariable String glossaryUid) { - List result = service.findAllTerms(glossaryUid); + public CommonApiResponse> retrieveTerms( + @PathVariable String glossaryUid, + @PageableDefault(size = Integer.MAX_VALUE, sort = "term") Pageable pageable + ) { + List result = service.retrieveTerms(glossaryUid, pageable); return CommonApiResponse.okWithResult(result); } } diff --git a/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryWebService.java b/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryWebService.java index 306fe94..1e0e0be 100644 --- a/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryWebService.java +++ b/api/src/main/java/vook/server/api/web/routes/glossary/GlossaryWebService.java @@ -1,6 +1,7 @@ package vook.server.api.web.routes.glossary; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import vook.server.api.app.GlossaryService; @@ -23,9 +24,9 @@ public List retrieve() { return RetrieveResponse.from(glossaries); } - public List findAllTerms(String glossaryUid) { + public List retrieveTerms(String glossaryUid, Pageable pageable) { Glossary glossary = glossaryService.findByUid(glossaryUid).orElseThrow(); - List terms = termService.findAllBy(glossary); - return FindAllTermsResponse.from(glossary, terms); + List terms = termService.findAllBy(glossary, pageable); + return RetrieveTermsResponse.from(glossary, terms); } } diff --git a/api/src/main/java/vook/server/api/web/routes/glossary/FindAllTermsResponse.java b/api/src/main/java/vook/server/api/web/routes/glossary/RetrieveTermsResponse.java similarity index 77% rename from api/src/main/java/vook/server/api/web/routes/glossary/FindAllTermsResponse.java rename to api/src/main/java/vook/server/api/web/routes/glossary/RetrieveTermsResponse.java index a42f336..4d74610 100644 --- a/api/src/main/java/vook/server/api/web/routes/glossary/FindAllTermsResponse.java +++ b/api/src/main/java/vook/server/api/web/routes/glossary/RetrieveTermsResponse.java @@ -10,7 +10,7 @@ import java.util.List; @Getter -public class FindAllTermsResponse { +public class RetrieveTermsResponse { private String term; @@ -23,14 +23,14 @@ public class FindAllTermsResponse { private String createdBy; - public static List from(Glossary glossary, List terms) { + public static List from(Glossary glossary, List terms) { return terms.stream() .map(term -> from(glossary, term)) .toList(); } - public static FindAllTermsResponse from(Glossary glossary, Term term) { - FindAllTermsResponse response = new FindAllTermsResponse(); + public static RetrieveTermsResponse from(Glossary glossary, Term term) { + RetrieveTermsResponse response = new RetrieveTermsResponse(); response.term = term.getTerm(); response.synonyms = term.getSynonyms().stream().map(TermSynonym::getSynonym).toList(); response.meaning = term.getMeaning();