동적 쿼리 처리 Querydsl

duckbill413·2023년 2월 25일
0

Spring JPA

목록 보기
8/8
post-thumbnail

Querydsl

데이터베이스를 이용해야 할 때 JPA나 JPQL을 이용하면 SQL을 작성하거나 쿼리를 처리하는 소스 부분이 줄어들기 때문에 편리하지만 어노테이션을 이용해서 지정하기 때문에 고정된 형태라는 단점이 있다.
예를 들면 검색 조건이 하나인 경우와 여러가지가 복합적으로 필요한 경우가 될 수 있는데, 만일 여러 종류의 속성들이 존재한다면 모든 경우의 수를 별도의 메소드로 작성하는 일을 어려울 수 있습니다.
이러한 문제는 JPQL이 정적으로 고정되기 때문입니다. Querydsl은 JPA의 구현체인 Hibernate 프레임워크가 사용하는 HQL(Hibernate Query Language)을 동적을 생성할 수 있는 프레임워크지만 JPA를 지원하고 있습니다. Querydsl을 이용하면 자바 코드를 이용하기 때문에 타입의 안정성을 유지한 상태에서 원하는 쿼리를 작성할 수 있습니다.
Querydsl을 이용하기 위해서는 Q도메인이라는 존재가 필요한데 Q도메인은 Querydsl의 설정을 통해서 기존의 엔티티 클래스를 Querydsl에서 사용하기 위해서 별도의 코드로 생성하는 클래스입니다.

장점

  • 문자가 아닌 코드로 쿼리를 작성하여 컴파일 시점에 문법 오류 확인 가능
  • 동적 쿼리 작성이 편리하다.
  • 쿼리 작성시 제약 조건 증을 메서드 추출을 통해 재사용할 수 있다.

Querydsl 설정

Querydsl의 build.gradle설정은 아래의 포스트에서 확인할 수 있습니다.
☘️Querydsl 설정하기

기존의 Repository와 Querydsl 연동하기

  1. Querydsl을 이용할 인테피이스 선언
  2. '인터페이스 이름 + Impl'이라는 이름으로 클래스를 선언 - 이때 QuerydslRepositorySupport라는 부모 클래스를 지정하고 인터페이스를 구현
  3. 기존의 Repository에는 부모 인터페이스로 Querydsl을 위한 인터페이스 지정

Board(게시판) Entity를 사용하여 페이지 처리 실습

  1. BoardSearch 인터페이스 선언
public interface BoardSearch {
    Page<Board> search1(Pageable pageable);
    Page<Board> searchAll(String [] types, String keyword, Pageable pageable);
}
  1. BoardSearchImpl 실제 구현 클래스 생성
    • 구현 클래스의 이름은 반드시 인터페이스 이름 + Impl로 작성해야 오류가 발생하지 않는다.
public class BoardSearchImpl extends QuerydslRepositorySupport implements BoardSearch {
    public BoardSearchImpl() {
        super(Board.class);
    }

        @Override
    public Page<Board> searchAll(String[] types, String keyword, Pageable pageable) {
        QBoard board = QBoard.board;
        JPQLQuery<Board> query = from(board);

        if ((types != null && types.length > 0) &&
                keyword != null) {// 검색 조건과 키워드가 있다면
            BooleanBuilder booleanBuilder = new BooleanBuilder();

            for (String type : types) {
                switch (type) {
                    case "t":
                        booleanBuilder.or(board.title.contains(keyword));
                        break;
                    case "c":
                        booleanBuilder.or(board.content.contains(keyword));
                        break;
                    case "w":
                        booleanBuilder.or(board.writer.contains(keyword));
                        break;
                }
            }
            // end for
            query.where(booleanBuilder);
        }// end if

        // bno > 0
        query.where(board.bno.gt(0L));
        // paging
        this.getQuerydsl().applyPagination(pageable, query);

        List<Board> list = query.fetch();
        long count = query.fetchCount();

        return new PageImpl<>(list, pageable, count);
    }
}
  • JPQLQuery@Query로 작성했던 JPQL을 코드를 통해서 생성할 수 있게 합니다. 이를 통해서 where이나 group by 혹은 join 처리 등이 가능합니다.
  • JPQLQuery의 실행은 fetch()라는 기능을 이용하고, fetchCount()를 이용하면 count 쿼리를 실행할 수 있습니다.

BooleanBuilder

  • 게시물에서 다양한 검색 조건(제목, 내용, 작성자)의 조합을 통해서 이루어진다면 Querydsl을 이용할 때 ()가 필요한 상황에서 BooleanBuilder을 이용해서 작성할 수 있다.
    • where 조건에서 and와 or이 섞여 있는 경우 연산자의 우선 순위가 다르기 때문에 or 조건은 ()로 묶어서 하나의 단위를 만들어 주는 것이 좋다.
	select
        b1_0.bno,
        b1_0.content,
        b1_0.moddate,
        b1_0.regdate,
        b1_0.title,
        b1_0.writer 
    from
        board b1_0 
    where
        (
            b1_0.title like ? escape '!' 
            or b1_0.content like ? escape '!' 
            or b1_0.writer like ? escape '!'
        ) 
        and b1_0.bno>? 
    order by
        b1_0.bno desc limit ?
  • titlecontent는 or 검색으로 bno는 and 검색을 하는 것을 확인할 수 있다. 또한, bno를 이용한 paging 처리도 desc limit을 통해 확인해 볼 수 있다.

PageImpli을 이용한 Page<T>반환

Querydsl에서는 페이징 처리를 직접 해야하는 불편함이 있다. 이를 위하여 Spring Data JPA의 PageImpl 클래스를 제공해서 3개의 파라미터로 Page<T>를 생성할 수 있습니다.

  • List<T>: 실제 목록 데이터
  • Pageable: 페이지 관련 정보를 가진 객체
  • long: 전체 개수

    new PageImpl<>(list, pageable, count);

  1. 기존의 BoardRepository의 선언부에 BoardSearch 인터페이스를 추가로 지정
public interface BoardRepository extends JpaRepository<Board, Long>, BoardSearch {
   
}
profile
같이 공부합시다~

0개의 댓글