[TIL-Springboot] JPQLQuery 키워드 조회

이용준·2023년 7월 11일
0

TIL

목록 보기
20/21

JPQL 검색

여러 엔티티 타입을 JPQL로 직접 처리하는 경우 Obejct[] 타입(흔히 Tuple)으로 나오므로 작성하는 방법 자체가 다르고 복잡하다. 반면 어떤 상황에서도 사용할 수 있는 가장 강력한 JPQL 구성할 수 있는 방식이라 할 수 있다. JPQL 사용하기 위해서는 build.gradle 파일에서 Querydsl 설정 추가해야 한다.

Repository 확장 방법

  1. 쿼리 메서드나 @Query 등 처리 불가 기능은 별도의 인터페이스로 설계
  2. 별도 인터페이스에 대한 구현 클래스 작성.(이때 QuerydslRepositorySupprot 클래스를 부모 클래스로 작성)
    - QuerydslRepositorySupprot 클래스는 Querydsl 라이브러리 통해 직접 무언가 구현할 때 사용
    - 생성자가 존재하므로 super() 이용해 호출 필요
    public class SampleRepositoryImpl extends QuerydslRepositorySupport implements SampleRepository{
      public SampleRepositoryImpl(){super(Board.class);}
    }
  3. 구현 클래스에 인터페이스 기능을 Q도메인 클래스 및 JPQLQuery 이용해 구현
    public Board Search(){
      QBoard board = QBoard.board;
      ...
    }

searchPage()

  • SearchBoardRepository에 파라미터와 리턴 타입 반영하는 메서드
  • 원하는 파라미터(Pageable) 전송하고, Page<Object[]> 만들어 반환하기
  • 검색 타입(type)과 키워드(keyword), 페이징 정보(Pageable) 파라미터로 구성
  • SearchBoardRepository.class
public interface SearchBoardRepository{
  Page<Object[]> searchPage(String type, String   Keyword, Pageable pageable);
}  
  • SearchBoardRepositoryImpl.class (구현부)
    • BooleanExpression 통한 조건 처리
public class SearchBoardRepositoryImpl extends QuerydslRepositorySupport implements SearchBoardRepository{

  @Override
  public Page<Object[]> searchPage(String tyep,   String keyword, Pageable pageable){
    log.info(" ============== SearchPage =============");
  
    QBoard board = QBoard.board;
    QReply reply = QReply.reply;
    QMember member = QMember.member;
  
    JPQLQuery<Board> jpqlQuery = from(board);
    jpqlQuery.leftjoin(member).on(board.writer.wq(member));
    jpqlQuery.leftjoin(reply).on(reply.board.eq(board));
    
    JPQLQuery<Tuple> tuple = jpqlQuery.select(board, member, reply.count());
    
    BooleanBuilder booleanBuilder = new Booleanbuilder();
    BooleanExpression expression = board.bno.gt(0L);
    booleanBuilder.and(expression);
    
    // 검색 조건 작성
    if(type != null){
      String[] typeArr = type.split("");
      
      BooleanBuilder conditionBuilder = new BooleanBuilder();
      
      for(String t:typeArr){
        switch(t){
          case "t":
            conditionBuilder.or(board.title.contains(keyword));
            break;
          case "w":
            conditionBuilder.or(board.email.contains(keyword));
            break;
          case "c":
            conditionBuilder.or(board.content.contains(keyword));
            break;
        }
      }
      booleanBuilder.and(conditionBuilder);
    }
    tuple.where(booleanBuilder);
    tuple.groupby(board);
    
    // order by
    Sort sort = pageable.getSort();
    
    // tuple.orderBy(board.bno.desc());
    sort.stream().forEach(order->{
      Order direction = order.isAscending() ? Order.ASC : Order.DESC;
      String prop = order.getProperty();
      
      PathBuilder orderByExpression = new PathBuilder(Board.class, "board");
      tuple.orderBy(new OrderSpecifier(direction, orderByExpression.get(prop)));
    });
    tuple.groupBy(board);
    
    // page 처리
    tuple.offset(pageable.getOffset());
    tuple.limit(pageable.getPageSize());
    
    List<Tuple> result = tuple.fetch();
    
    log.info(result);
    
    long count = tuple.fetchCount();
    
    log.info("COUNT : "+count);
    
    return new PageImpl<Object[]>(
        result.stream().map(t -> t.toArray()).collect(Collectors.toList()),
        pageable,
        count
    );
  }
}
  • Tuple 객체

    • 정해진 엔티티 객체 단위가 아닌 각각의 데이터를 추출하는 경우 사용
    // 기존
    List<Board> result = jpqlQuery.fetch();
    // 변경  
    List<tuple> result = tuple.fetch();
  • BooleanBuilder

    • 조건(where)컨테이너 설정
    BooleanBuilder booleanBuilder = new BooleanBuilder();
    BooleanExpression expression = board.bno.gt(0L);
    
    booleanBuilder.and(expression);
  • BooleanExpression

    • 필드값과 결합한 조건 생성
    BooleanExpression expression = qBoard.title.contains(keyword);
  • Sort

    • JPQL에서는 Sort 객체 지원하지 않으므로 orderBy()의 경우OrderSpecifier<T extends Comparable> 파라미터로 처리해야 함.
    // Order by
    Sort sort = pageable.getSort();
    
    sort.stream().forEach(order -> {
      Order direction = order.isAsecnding() ? Order.ASC : Order.DESC;
      String prop = order.getProperty();
      
      PathBuilder = orderByExpression = new PathBuilder(Board.class, "board");
      tuple.orderBy(new OrderSpecifier(direction, orderByExpression.get(prop)));
    });
    1. ~.domain.Sort는 내부적으로 여러개의 Sort 객체 연결 가능하므로 forEach() 사용해 처리
    2. OrderSpecifier는 정렬이 필요하므로 Sort 객체의 정렬 정보를 ~.types.Order 타입으로 처리
    3. Sort 객체 속성(bno, title) 등은 PathBuilder 통해 처리
      • PathBuilder 생성시 문자열로 된 이름은 JPQLQuery 생성시 이용하는 변수명과 동일

Test 코드

  • BoardRepositoryTests
public void testSearchPage(){
  Pageable pageable = PageRequest.of(0,10,Sort.by("bno").descending().and(Sort.by("title").ascending()));
  
  Page<Object[]> result = boardRepository.searchPage("t", "1", pageable);  
  
}
profile
뚝딱뚝딱

0개의 댓글