[Java/SpringBoot] JPA 검색기능 구현하기

minjung·2022년 11월 27일
0
post-thumbnail

💡JPA 기본 검색 기능 구현하기

QuerydslPredicateExecutor

기본 검색기능은 위 클래스를 상속(extends)받음으로써 구현할 수 있다.

public interface ArticleRepository extends
        JpaRepository<Article, Long>,
        QuerydslPredicateExecutor<Article>{}

이런 식으로 뒤에 엔티티를 적어주는데, 이 엔티티 안에 있는 모든 필드에 대한 기본 검색기능을 추가해준다.

이 때 기본 검색기능은 대소문자를 구분하지 않고, 전체 입력값 검색을 의미한다.
전체 입력값 검색이란, 부분검색이 되지 않음을 말한다.


HAL Explorer를 사용해서 테스트 해보았다.


  • 제목 전체를 입력값으로 했을 때 해당 제목의 게시글만 검색되는 것을 볼 수 있다.

  • 대소문자를 구분하지 않는다.

  • 하지만 전체 내용 중 일부만 입력하면 검색되지 않는다.

💡JPA 검색 기능 세부 설정하기

QuerydslBinderCustomizer

기본 검색기능에서 설정하지 못한 세부 기능을 설정할 수 있다.
마찬가지로 QuerydslBinderCustomizer를 상속받는다.

public interface ArticleRepository extends
        JpaRepository<Article, Long>,
        QuerydslPredicateExecutor<Article>,
        QuerydslBinderCustomizer<QArticle> {}

자바는 원래 단일상속만 가능하지만, 인터페이스에서는 다중상속이 가능하다.

그리고 인터페이스 안에서 QuerydslBinderCustomizercustomize 메소드를 override 함으로써 검색에 대한 세부 기능을 재정의 할 수 있다.

원래는 인터페이스 파일이라 이 안에서 원래 구현을 넣을 수 없지만, Java8부터 가능해졌다.


@Override customize 코드 설명

  • 전체 코드

@Override
default void customize(QuerydslBindings bindings, QArticle root){
	bindings.excludeUnlistedProperties(true);

	bindings.including(root.title, root.content, root.hashtag, root.createdAt, root.createdBy);

	bindings.bind(root.title).first(StringExpression::containsIgnoreCase);
	bindings.bind(root.content).first(StringExpression::containsIgnoreCase);
	bindings.bind(root.hashtag).first(StringExpression::containsIgnoreCase);
	bindings.bind(root.createdAt).first(DateTimeExpression::eq);
	bindings.bind(root.createdBy).first(StringExpression::containsIgnoreCase);

}

  • excludeUnlistedProperties

bindings.excludeUnlistedProperties(true);

현재 QuerydslPredicateExecutor(기본검색기능)에 의해서 엔티티에 있는 모든 필드에 대한 검색이 되고 있다. 우리가 원하는 필드만 설정하기 위해서 사용한다.
true로 하면 list하지 않은 프로퍼티는 검색에서 제외한다. (기본값은 false)


  • including

bindings.including(root.title, root.content, root.hashtag, root.createdAt, root.createdBy);

검색을 원하는 필드를 추가한다.


  • likeIgnoreCase, containsIgnoreCase

bindings.bind(root.title).first(StringExpression::likeIgnoreCase); // like ''

bindings.bind(root.title).first(StringExpression::containsIgnoreCase); // like '%%'

부분검색과 대소문자 구분을 하지 않게끔 하는 코드이다.
이 둘의 차이는 쿼리문이 다르다는 것이다.

부분검색을 하려면 검색어에 %를 넣어주어야 하는데, likeIgnoreCase를 사용하면 이 %를 내가 직접 넣어주어야 한다.
% 넣는 것을 수동으로 정하고 싶을 때에는 likeIgnoreCase를 사용하고, 그렇지 않을 경우에는 containsIgnoreCase를 사용하면 된다.


💡완성

세부검색 기능 설정까지 완료하면 이렇게 일부만 입력해도 해당 입력값이 들어간 데이터를 검색할 수 있다. IgnoreCase 설정으로 인해 대소문자도 구분하지 않는다.


💡최종 코드

package com.fastcampus.projectboard.repository;

import com.fastcampus.projectboard.domain.Article;
import com.fastcampus.projectboard.domain.QArticle;
import com.querydsl.core.types.dsl.DateTimeExpression;
import com.querydsl.core.types.dsl.StringExpression;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource
public interface ArticleRepository extends
        JpaRepository<Article, Long>,
        QuerydslPredicateExecutor<Article>, // 엔티티 안에 있는 모든 필드에 대한 기본 검색기능을 추가해준다. 대소문자를 구분하지는 않는다. 하지만 부분검색이 안된다.
        QuerydslBinderCustomizer<QArticle> {

    // 검색에 대한 세부 기능 재정의
    // 인터페이스 파일이라 이 안에서 원래 구현을 넣을 수 없지만, Java8부터 가능해짐
    @Override
    default void customize(QuerydslBindings bindings, QArticle root){
        bindings.excludeUnlistedProperties(true);
        // 현재 QuerydslPredicateExecutor에 의해서 엔티티에 있는 모든 필드에 대한 검색이 되고 있다. 우리가 원하는 필드만 설정하기 위해서 사용한다.
        // true로 하면 list하지 않은 프로퍼티는 검색에서 제외한다. (기본값은 false)

        bindings.including(root.title, root.content, root.hashtag, root.createdAt, root.createdBy); // 검색을 원하는 필드를 추가한다.

        // 이 둘의 차이는 쿼리문이 다르다.
//        bindings.bind(root.title).first(StringExpression::likeIgnoreCase); // like ''
        bindings.bind(root.title).first(StringExpression::containsIgnoreCase); // like '%%'
        // 부분검색을 하려면 검색어에 %를 넣어주어야 하는데, like를 사용하면 이 %를 내가 직접 넣어주어야 한다.
        // % 넣는 것을 수동으로 정하고 싶을 때에는 like를 사용하고, 그렇지 않을 경우에는 contains를 사용하면 된다.
        bindings.bind(root.content).first(StringExpression::containsIgnoreCase);
        bindings.bind(root.hashtag).first(StringExpression::containsIgnoreCase);
        bindings.bind(root.createdAt).first(DateTimeExpression::eq);
        bindings.bind(root.createdBy).first(StringExpression::containsIgnoreCase);

    }

}

0개의 댓글