데이터베이스를 이용해야 할 때 JPA나 JPQL을 이용하면 SQL을 작성하거나 쿼리를 처리하는 소스 부분이 줄어들기 때문에 편리하지만 어노테이션을 이용해서 지정하기 때문에 고정된 형태라는 단점이 있다.
예를 들면 검색 조건이 하나인 경우와 여러가지가 복합적으로 필요한 경우가 될 수 있는데, 만일 여러 종류의 속성들이 존재한다면 모든 경우의 수를 별도의 메소드로 작성하는 일을 어려울 수 있습니다.
이러한 문제는 JPQL이 정적으로 고정되기 때문입니다. Querydsl은 JPA의 구현체인 Hibernate 프레임워크가 사용하는 HQL(Hibernate Query Language)을 동적을 생성할 수 있는 프레임워크지만 JPA를 지원하고 있습니다. Querydsl을 이용하면 자바 코드를 이용하기 때문에 타입의 안정성을 유지한 상태에서 원하는 쿼리를 작성할 수 있습니다.
Querydsl을 이용하기 위해서는 Q도메인이라는 존재가 필요한데 Q도메인은 Querydsl의 설정을 통해서 기존의 엔티티 클래스를 Querydsl에서 사용하기 위해서 별도의 코드로 생성하는 클래스입니다.
Querydsl의 build.gradle
설정은 아래의 포스트에서 확인할 수 있습니다.
☘️Querydsl 설정하기
BoardSearch
인터페이스 선언public interface BoardSearch {
Page<Board> search1(Pageable pageable);
Page<Board> searchAll(String [] types, String keyword, Pageable pageable);
}
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을 이용해서 작성할 수 있다.()
로 묶어서 하나의 단위를 만들어 주는 것이 좋다.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 ?
- title과 content는 or 검색으로 bno는 and 검색을 하는 것을 확인할 수 있다. 또한, bno를 이용한 paging 처리도 desc limit을 통해 확인해 볼 수 있다.
Page<T>
반환Querydsl에서는 페이징 처리를 직접 해야하는 불편함이 있다. 이를 위하여 Spring Data JPA의 PageImpl
클래스를 제공해서 3개의 파라미터로 Page<T>
를 생성할 수 있습니다.
List<T>
: 실제 목록 데이터Pageable
: 페이지 관련 정보를 가진 객체long
: 전체 개수
new PageImpl<>(list, pageable, count);
public interface BoardRepository extends JpaRepository<Board, Long>, BoardSearch {
}