[점프 투 스프링부트] 답변에 페이징이 쉽지가 않습니다.

1번 방법. AnswerList와 Repository를 사용하는 방법
실패 사유 : Answer 엔티티에서 Question 엔티티를 참조한 속성명이므로 QuestionRepository에서 사용 불가

2번 방법. Answer 엔티티에서 Page 기능 삽입
실패 사유 : Question id와 연계하여 페이징을 해야 하는데 방법을 모르겠습니다.
(연계가 안되어 있어서 모든 ANSWER 엔티티가 나옵니다.)

여기서 어떤 점을 개선해야 될까요.....

Imgur

[AnswerRepository]

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AnswerRepository extends JpaRepository<Answer, Integer> {
    Page<Answer> findAll(Pageable pageable);
}

[AnswerService]

@RequiredArgsConstructor
@Service
public class AnswerService {
    private final AnswerRepository answerRepository;

        public Page<Answer> getList(int page) {
        List<Sort.Order> sorts = new ArrayList<>(); 
        sorts.add(Sort.Order.desc("Voter")); 
        sorts.add(Sort.Order.desc("createDate")); 
        Pageable pageable = PageRequest.of(page, 3, Sort.by(sorts)); 
      return this.answerRepository.findAll(pageable);
    }

[QuestionController]

@RequestMapping("/question")
@RequiredArgsConstructor 
@Controller
public class QuestionController {
    private final QuestionService questionService;
    private final AnswerService answerService; //답변 페이징 기능을 위해 추가
    private final UserService userService;

    @RequestMapping(value = "/detail/{id}")
    public String detail(Model model, @PathVariable("id") Integer id, AnswerForm answerForm, @RequestParam(value="page", defaultValue="0") int page) {
        Question question = this.questionService.getQuestion(id); 
        Page<Answer> paging = this.answerService.getList(page);
        model.addAttribute("paging", paging);
        model.addAttribute("question", question);
        return "question_detail";
    }

ElectricComputer 668

2022년 7월 12일 12:09 오전

목록으로
2개의 답변이 있습니다. 1 / 1 Page

AnswerRepository에는 다음을 추가하고

Page<Answer> findAll(Question question, Pageable pageable);

AnswerService는 다음처럼 수정해 보세요.

    public Page<Answer> getList(Question question, int page) {
        List<Sort.Order> sorts = new ArrayList<>();
        sorts.add(Sort.Order.desc("Voter"));
        sorts.add(Sort.Order.desc("createDate"));
        Pageable pageable = PageRequest.of(page, 3, Sort.by(sorts));
        return this.answerRepository.findAll(question, pageable);
    }

그리고 상세화면 컨트롤러에서 답변 페이징 조회시 Question 객체를 추가로 전달해 보세요.

수정: findAll 이 아니고 findAllByQuestion으로 고쳐주세요.

박응용

M 2022년 7월 12일 10:52 오전

@박응용님 감사합니다. AnswerRepository를 수정해봤는데 다음 에러가 나옵니다.......ㅠㅠ org.springframework.data.mapping.PropertyReferenceException: No property 'findAll' found for type 'Answer'! 이러한 오류로 인해 findAll을 findAllBy로 수정하여 실행했는데 아래 에러가 나옵니다 ㅠㅠ Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback. Tue Jul 12 01:48:42 KST 2022 There was an unexpected error (type=Internal Server Error, status=500). At least 1 parameter(s) provided but only 0 parameter(s) present in query.; nested exception is java.lang.IllegalArgumentException: At least 1 parameter(s) provided but only 0 parameter(s) present in query. org.springframework.dao.InvalidDataAccessApiUsageException: At least 1 parameter(s) provided but only 0 parameter(s) present in query.; nested exception is java.lang.IllegalArgumentException: At least 1 parameter(s) provided but only 0 parameter(s) present in query. 컨트롤러는 아래와 같이 수정했습니다. ``` @RequestMapping("/question") @RequiredArgsConstructor @Controller public class QuestionController { private final QuestionService questionService; private final AnswerService answerService; //답변 페이징 기능을 위해 추가 private final UserService userService; @RequestMapping(value = "/detail/{id}") public String detail(Model model, @PathVariable("id") Integer id, AnswerForm answerForm, @RequestParam(value="page", defaultValue="0") int page) { Question question = this.questionService.getQuestion(id); Page<Answer> paging = this.answerService.getList(question, page); model.addAttribute("paging", paging); model.addAttribute("question", question); return "question_detail"; } ``` - ElectricComputer님, M 2022년 7월 12일 10:12 오전 추천 , 대댓글
+1 @ElectricComputer님 findAll 이 아니라 findAllByQuestion이네요. - 박응용님, 2022년 7월 12일 10:51 오전 추천 , 대댓글
@박응용님 감사합니다! 시원하게 해결되었습니다. - ElectricComputer님, 2022년 7월 12일 7:00 오후 추천 , 대댓글
@박응용님 자주 여쭤봐서 죄송합니다. 답변 및 페이징 기능까지 구현 후 4장 SBB에 대해 교재를 참고하여 진행하고자 합니다. 다만 그전에.....이미 구현한 기능 중에서 제가 잘못 구현한 부분에 대해 문의드립니다. ㅠㅠ 위 글에서 아래와 같이 구현했는데 Voter가 Set인지라 의도한 추천 순으로 정렬이 되지 않습니다. sorts.add(Sort.Order.desc("Voter")); 이에 Voter.size()를 담을 수 있는 엔티티를 구현해야 하는지 Repository에 Voter.size()를 호출할 수 있는 것을 구현해야 하는지 JPQL로 커스텀 쿼리를 통해 voter.size를 해야 하는지 서비스 자체에서 해결해야 되는지 잘 모르겠습니다. 혹시 추천순 정렬에 대해 어떠한 방식으로 구현해야 하는지 문의드려도 될까요? ㅠㅠ 죄송합니다. - ElectricComputer님, M 2022년 7월 13일 1:50 오전 추천 , 대댓글

추천순으로 소트하려면 복잡하기 때문에 직접 쿼리를 작성하는것을 추천합니다.

AnswerRepository에 findAllByQuestion 메서드를 다음과 비슷하게 작성하시면 될것 같아요.

@Query(value="select "
            + "distinct a.*, count(av.answer_id) as voter_count "
            + "from answer a "
            + "left outer join answer_voter av on av.answer_id=a.id "
            + "where a.question_id = :questionId "
            + "group by a.id, av.answer_id "
            + "order by voter_count desc, a.create_date desc "
            , countQuery = "select count(*) from answer"
            , nativeQuery = true)
    Page<Answer> findAllByQuestion(@Param("questionId") Integer questionId, Pageable pageable);

그리고 서비스에서 questionId를 전달하여 호출하면 됩니다. 그리고 Sort는 쿼리에서 했으니까 서비스에서 pageable에 등록한 Order by 항목들은 제거해야 합니다.

박응용

M 2022년 7월 13일 3:59 오후

+1 @박응용님 감사합니다 덕분에 해결되었습니다. 점프 투 자바, 점프 투 파이썬 등 서적들 덕분에 프로그래밍에 재미를 붙이고 있습니다. 감사합니다! countQuery에서 (where answer.question_id = :questionId) WHERE절을 추가하여 특정한 질문의 답변 1페이지에서 게시판에 존재하는 모든 질문 댓글 페이지가 나오는 것을 방지했습니다. ```java @Query(value="select " + "distinct a.*, count(av.answer_id) as voter_count " + "from answer a " + "left outer join answer_voter av on av.answer_id=a.id " + "where a.question_id = :questionId " + "group by a.id, av.answer_id " + "order by count(av.answer_id) desc, a.create_date desc " , countQuery = "select count(*) from answer where answer.question_id = :questionId" , nativeQuery = true) Page<Answer> findAllByQuestion(@Param("questionId") Integer questionId, Pageable pageable); ``` - ElectricComputer님, 2022년 7월 13일 8:05 오후 추천 , 대댓글