[Spring] 다중 검색 처리를 위한 동적쿼리를 만들고 싶다.

신창호·2024년 2월 4일
0

Spring

목록 보기
4/9

사이드프로젝트의 기존기능(RestTemplate 비동기처리)을 마치고, 게시판 기능으로 넘어왔다.

이번에는 DB에 있는 데이터를 가져와 활용을 해야되는 것이기 때문에 JPA를 사용해서 만들어보고 있는데,

가장먼저 맞닿드린 것은 "검색 기능"!

검색 기능

그냥 간단한 조회 기능의 경우, 조건 하나만 가지고 불러오면 쉽게 구현할 수 있지만,
대부분, 조회에는 필터를 걸 수 있고 나또한, 필터 기능이 있는 조회를 하고자 한다.

하지만, 필터의 조건이 4개만 되어도
하나만 고른 경우 4가지
두개의 필터를 고른 경우 6가지
세개의 필터를 고른 경우 4가지
전부 다 1가지
= 15가지의 경우의 수가 나온다.
사실 이정도만 해도 직접 하나하나 구현할만 하다.

하지만, 나의 경우는 필터의 조건이 더 많아 JPA Repository에 인자만 다른 메소드를 반복해서 만드는 모습을 보며 이게 맞나? 라는 생각이 들었다.


사전지식 JPQL

  • 동적쿼리로 넘어가기 전에 JPQL에 대해 개념을 어느정도 알고 들어가야 이해하기 쉽다.
  • JPQL(Java Persistence Query Language) = java로 구현할 수 있는 쿼리언어이며, SQL과 비슷한 문법을 가지고 있지만, JPQL은 데이터베이스 테이블이 아닌 엔티티 객체를 대상으로 작동하기때문에,
    데이터베이스의 스키마개념과 코드의 개념간의 불일치 문제를 해결하여 사용할 수 있다.

주요 특징

  • 객체 지향 쿼리 언어: JPQL은 객체 지향적인 특성을 가진다.
    • 아래 예시처럼, 엔티티(클래스)와 속성을 중심으로 작성되게 된다.
  • 데이터베이스 중립성: JPQL은 특정 데이터베이스에 종속되지 않는다.
    • 데이터베이스마다 언어문법이 다르기때문에, 데이터베이스마다 각각 다른 코드를 작성해야되지만,JPQL의 경우 JPA가 연결된 DB의 SQL로 번역을 해주기때문에, 어떤 DB를 쓰더라도 JPQL만 알면되기에 매우 좋다.
  • 타입 세이프: JPQL은 컴파일 시점에 쿼리의 문법 오류를 검출가능
    • 하지만, 문제점이 있음.

예시

   String jpql = "SELECT m FROM Member m WHERE m.age > 18";
   List<Member> members = entityManager.createQuery(jpql, Member.class).getResultList();

이렇게 결국 JPQL은 기본 문자열로 작성되기 때문에, 컴파일시 에러가 발생되지 않는다.
그리고 문자열이기때문에, 상황에따라 수정이 가능한데, 이것을 활용하여 동적으로 변경할 수 있다.

동적쿼리

  • 결국, 조회한다는 것은 DB에 쿼리로 필요한 데이터를 가져온다는 것!

  • 그 가져오는 쿼리를 조건에 맞춰 쓸수 있게 할 수 있는데, 이것이 동적쿼리이다!

  • 당연히,Spring JPA에도 동적쿼리를 생성하기 위해 여러가지 기능을 제공한다는 것을 알게되었다.

    • (MyBatis의 경우는 동적쿼리를 쉽게 만들 수 있는 점을 보아, 진짜 장단점이 있는 것 같다.)
  • 기본적으로 조건에 따라 JPQL의 문자열을 수정하여 만든다.



1번째 방법, JpaSpecificationExecutor

  • Spring Data JPA에서 제공해주는 인터페이스로,별도의 설정없이도 쉽게 사용하여 동적쿼리를 만들 수 있다.
  • JPQL를 문자열로만 수정하니, 코드가 길어지고, 가독성이 떨어지니, java코드로 수정할 수 있게 만들어 주는Criteria API 사용하여 동적쿼리를 만든다.
    • 하지만, 이녀석도 가독성이 좋은 편은 아님.
  • 하지만, 이 방식은 사용하려고 하지 않을려고 한다.

단점

  • 과거 프로젝트에도 specification을 사용해본적은 있는데, 확실히 코드가 많이 줄어든게 느껴지긴했지만, 구현과정에서 에러를 많이 맞닿들렸고, 생각보다 복잡하고 어려워서 다시 보고하려면 어려움이 있었다.
    • 이런 점을 봤을때, 다른 사람과 협업하거나 유지보수하는데 더 많은 시간이 들어감을 느끼고 쓰임을 지양하는 편이며, 실제로 주변 개발자 지인들에게도 이야기했을 때, 별로 좋아하진 않았다~


2번째 방법, QueryDSL 사용

  • 내가 이번 프로젝트에 적용해보고자하는 방식이다.
  • 워낙 직관적이지 않은 Criteria API가 불편하여 만들어진 QueryDSL이다.
    • 하지만, 내부적으로는 결국 Criteria 기반으로 만들어졌다.

사용법

  • 라이브러리 의존성을 추가해줘야한다.

  • q파일을 생성할 디렉토리 설정도 해줘야한다.

  • 이제 생성된 q객체를 가지고, 쿼리를 짜면된다.

	QOrder order = QOrder.order;
    QMember member = QMemeber.member;
    
    JPAQueryFactory query = new JPAQueryFactory(em);
    
    //조건
    
    query
    		.select(order)
            .from(order)
            .join(order.member, member)
            .where(statusEq(orderSearch.getOrderStatus()), nameLike(...))
            .limit(1000)
            .fetch();
            
   	private BooleanExpression statusEq(){
      if(statusCond == null){
      		return null
      }
      return QOrder.order.status.eq(statusCond);
    }

=> jpql로 바뀌어서 실행된다.

장점

  • 직관적인 문법
  • 컴파일 시점에 빠른 문법 오류 발견
  • 코드 자동완성
  • 코드의 재사용
  • JPQL new 명령어와는 비교가 안될 정보로 깔끔한 DTO 조회 지원

(일단 queryDSL의 맛만 봐본것이며, 계속해서 공부하다가 한번더 사용방법에 대해 올려보고자 한다.)
(다소 내용이 부실할 수 있는데, 이것 말고도 GCP서버작업도 해보느라.. 정리의 시간이 필요해, 먼저 끄적여봤다. )

profile
한단계씩 올라가는 개발자

0개의 댓글