[에러]Jpa 검색기능 like 사용시nativeQuery와 메소드 사용

Nam_JU·2022년 6월 24일
0

ErrorLog

목록 보기
20/26

에러배경

모모프로젝트를 하면서 검색기능을 구현하게 되었다.
쿼리자체는 간단한데 이걸 어떤방식으로 사용하는지 고민이 됐다. 내가 할 줄 아는 nativeQuery와 메소드를 사용하는 방법 두가지를 해보다가 에러가 났다.


먼저 원하는 형태의 결과값을 도출할 쿼리를 제작한다

select question from question_entity where question like '%학교%';

문제는 like%원하는 문자%의 형태인데 원하는 문자를 어떻게 JPA로 불러오는가가 고민이 되었다.


나의 경우 평소에 native쿼리를 주로 사용하여 결과값을 가져왔다.
그런데 면접을 보면서 왜 native쿼리를 사용했는지 질문을 받은적이 있었고 이것이 알고보니 JPQL이라는것을 알게됐다.

JPQL이란?

 JPQL은 엔티티 객체를 조회하는 객체지향 쿼리다. 따라서 테이블을 대상으로 쿼리하는 것이 아니라 엔티티 객체를 대상으로 쿼리한다. 문법은 SQL과 유사하며 간결하다. JPQL은 결국 SQL로 변환된다.

 또한 JPQL은 SQL을 추상화해서 특정 데이터베이스에 의존하지 않는다는 특징이 있다. 데이터베이스 방언만 변경하면 JPQL을 수정하지 않아도 데이터베이스를 변경할 수 있다.

Repository

public interface QuestionRepository extends CrudRepository<QuestionEntity,Long> {

//nativeQuery
    @Query(
            nativeQuery = true,
            value = "SELECT question FROM question_entity WHERE question like %:keyword%"
    )
    List<QuestionSearch>searchQuestion(@RequestParam("keyword") String keyword);


//queryMethod
    List<QuestionSearch> findByQuestionContains(@RequestParam("text") String text);

}

당시에는 innerjoin을 두번 사용했고 아는것이 nativeQuery밖에 없어 사용하였지만 이번처럼 쿼리가 짧은 경우는 queryMethod를 사용하는게 좋다.

비교를 하고싶어서 두가지를 모두 적용해보았다!


Interface

public interface QuestionSearch {

    String getQuestion();
}

결과값으로 불러올 컬럼명을 인터페이스에 적는다


Service


//nativeQuery
    @Override
    public Collection<QuestionSearch> searchQuestion(String keyword) {
        List<QuestionSearch> questionSearchList = this.questionRepository.searchQuestion(keyword);
        logger.info("ServiceKeyword: {}",keyword);
        return questionSearchList;
    }


//QueryMethod
    @Override
    public Collection<QuestionEntity> findByQuestionContaining(String text) {
        logger.info("serviceText: {}",text);
        List<QuestionEntity> questionSearchLists = this.questionRepository.findByQuestionContains(text);
        return questionSearchLists;
    }

Controller


@RestController
@RequestMapping("api/v1/searches")
@RequiredArgsConstructor
public class SearchController {
    private final QuestionService questionService;
    private final Logger logger = LoggerFactory.getLogger(SearchController.class);


//nativeQuery
    @GetMapping("questions/{keyword}")
    public ResponseEntity<Collection<QuestionSearch>> searchQuestion(@RequestParam("keyword") String keyword){
        Collection<QuestionSearch> result = this.questionService.searchQuestion(keyword);
        logger.info("keyword 검색: {}",keyword);
        return ResponseEntity.ok(result);
    }


//queryMethod
    @GetMapping("questions/test/{text}")
    public ResponseEntity<Collection<QuestionEntity>> findByQuestionContaining(@RequestParam("text") String text){
        Collection<QuestionEntity> result = this.questionService.findByQuestionContaining(text);
        logger.info("keyword 검색: {}",text);
        return ResponseEntity.ok(result);
    }
}

에러내용

  1. queryMethod는 아무런 응답이 없었다(혹은 400에러)
  2. nativeQuery는 uri가 자꾸 이상했다.
    아래의 사진처럼 requestmapping값/{아무거나}?keyword=검색어
    형태로 검색이되었다.


에러원인

Controller에서 uri문제

쿼리스트링은 @RequestParam이다
평소 @PathVariable을 사용하다보니 당연히 uri에도 PathVariable처럼 "{testId}"같은 형태가 있어야 한다고 생각했다.

이래서 습관이 무섭다고...
앞으로 주의하도록 하자

@RestController
@RequestMapping("api/v1/searches")
@RequiredArgsConstructor
public class SearchController {
    private final QuestionService questionService;
    private final Logger logger = LoggerFactory.getLogger(SearchController.class);


    @GetMapping("") //변경
    public ResponseEntity<Collection<QuestionSearch>> searchQuestion(@RequestParam("keyword") String keyword){
        Collection<QuestionSearch> result = this.questionService.searchQuestion(keyword);
        logger.info("keyword 검색: {}",keyword);
        return ResponseEntity.ok(result);
    }


    @GetMapping("questions/test") //변경
    public ResponseEntity<Collection<QuestionSearch>> findByQuestionContaining(@RequestParam("text") String text){
        Collection<QuestionSearch> result = this.questionService.findByQuestionContaining(text);
        logger.info("keyword 검색: {}",text);
        return ResponseEntity.ok(result);
    }
}

QueryMethod 400에러 문제

nativeQuery는 반환타입을 위의 인터페이스 QuestionSearch로 하였다. 그런데 왜인지 QueryMethod는 QuestioinEntity를 받아야 한다고 생각했었는데 .. ㅋㅋㅋ 똑같이 QuestionSearch인터페이스를 반환타입을 설정하니 정상적으로 작동하였다.

    @Override
    public Collection<QuestionSearch> searchQuestion(String keyword) {
        List<QuestionSearch> questionSearchList = this.questionRepository.searchQuestion(keyword);
        logger.info("ServiceKeyword: {}",keyword);
        return questionSearchList;
    }

    @Override			//변경
    public Collection<QuestionSearch> findByQuestionContaining(String text) {
        logger.info("serviceText: {}",text);
        List<QuestionSearch> questionSearchLists = this.questionRepository.findByQuestionContains(text);
        return questionSearchLists;
    }
}


에러 해결

둘다 잘 받아와지는 것을 볼 수 있다.
앞으로 짧은 쿼리의 경우는 QueryMethod를 사용하고 복잡한 쿼리의 경우는 nativeQuery나 QueryDsl을 공부하여 적용해봐야할것 같다



참고문서

https://leveloper.tistory.com/103

profile
개발기록

0개의 댓글