여러 엔티티 타입을 JPQL로 직접 처리하는 경우 Obejct[] 타입(흔히 Tuple)으로 나오므로 작성하는 방법 자체가 다르고 복잡하다. 반면 어떤 상황에서도 사용할 수 있는 가장 강력한 JPQL 구성할 수 있는 방식이라 할 수 있다. JPQL 사용하기 위해서는 build.gradle 파일에서 Querydsl 설정 추가해야 한다.
public class SampleRepositoryImpl extends QuerydslRepositorySupport implements SampleRepository{
public SampleRepositoryImpl(){super(Board.class);}
}
public Board Search(){
QBoard board = QBoard.board;
...
}
public interface SearchBoardRepository{
Page<Object[]> searchPage(String type, String Keyword, Pageable pageable);
}
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
BooleanBuilder booleanBuilder = new BooleanBuilder();
BooleanExpression expression = board.bno.gt(0L);
booleanBuilder.and(expression);
BooleanExpression
BooleanExpression expression = qBoard.title.contains(keyword);
Sort
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)));
});
~.domain.Sort
는 내부적으로 여러개의 Sort 객체 연결 가능하므로 forEach() 사용해 처리OrderSpecifier
는 정렬이 필요하므로 Sort 객체의 정렬 정보를 ~.types.Order
타입으로 처리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);
}