간단하다. BooleanBuilder를 사용하면 된다. and()가 새로운 괄호의 시작이다. and()를 사용하면 괄호로 묶인다고 생각하자.
프로젝트에서 페이징 정보(page, size, sort)를 Pageable(실제로 받은 것은 PageRequest로 예상된다)로 받을 수 있도록 하였다. 따라서 query-dsl과 pageable을 이용하여 아래와 같이 쿼리에 정렬을 적용해보았다.
Pageable 객체에 정렬조건 null 여부를 체크한 후 정렬 조건이 존재한다면 반복문을 통해 정렬 조건을 가져오고 이를 OrderSpecifier에 담아 반하도록 하였다. 현재 코드에서는 모든 정렬을 오름차순으로 설정하였지만 Pageable에서 얻어낸 정렬 조건에서 정렬 순서를 가져와 적용할 수 있다.
Order direction = order.getDirection().isAscending() ? Order.ASC : Order.DESC;
현재 코드에서는 오로지 하나의 정렬 조건만을 고려하고 있다. 다수의 정렬 조건이 적용된다면 아래와 같이 다른 방법을 적용해야 한다.
ex) /api/tutorials?page=0&size=3&sort=published,desc&sort=title,asc
private List<OrderSpecifier> getAllOrderSpecifiers(Pageable pageable) {
List<OrderSpecifier> ORDERS = new ArrayList<>();
if (!isEmpty(pageable.getSort())) {
for (Sort.Order order : pageable.getSort()) {
Order direction = order.getDirection().isAscending() ? Order.ASC : Order.DESC;
switch (order.getProperty()) {
case "id":
OrderSpecifier<?> orderId = QueryDslUtil.getSortedColumn(direction, QRoom.room, "id");
ORDERS.add(orderId);
break;
case "user":
OrderSpecifier<?> orderUser = QueryDslUtil.getSortedColumn(direction, QUser.user, "name");
ORDERS.add(orderUser);
break;
case "category":
OrderSpecifier<?> orderCategory = QueryDslUtil.getSortedColumn(direction, QRoom.room, "category");
ORDERS.add(orderCategory);
break;
default:
break;
}
}
}
return ORDERS;
}
...
// 정렬 조건 적용
orderBy(ORDERS.stream().toArray(OrderSpecifier[]::new))
class PageRequest extends AbstractPageRequest { … }
abstract class AbstractPageRequest implements Pageable, Serializable { … }
orderBy에 null 반환시 에러가 발생한다. null을 반화하지마라.
/api/tutorials?page=0&size=3&sort=published
와 같이 정렬 방향에 대한 정보를 입력하지 않은 경우 오름차순(ASC)이 적용된다.
org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: ~~~
와 같은 에러를 query-dsl을 사용하다가 만나게 되었다. 해당 문제에 대한 답변을 이 사이트에서 찾을 수 있었다. 일단 Invalid Path라는 것은 두 엔티티간의 관계가 무엇인지 모르겠다는 것을 의미한다고 한다. 이를 해결하기 위해서는 Querydsl에게 두 엔티티간의 관계를 알려주어야 한다고 한다. 즉, join 또는 fetch join할 경우 조인 대상만 명시하지말고 별칭으로 사용할 Q타입까지 명시해주면 해당 문제를 해결할 수 있다.
join(조인 대상, 별칭으로 사용할 Q타입)
만약 Dtype 값을 엔티티에서 필드로 직접 사용할 필요가 있다면 @Column(insertable = false, updatable = false)을 이용하여 읽기 전용으로 사용하면 된다.
컬렉션 페치 조인은 페이징이 불가능하다. 페이징을 시도한다면 모든 데이터를 메모리에 로딩을 하고 메모리에서 페이징을 하게된다. (굉장히 많은 데이터를 한번에 메모리에 올린다면…?)
처음에 위의 코드를 작성할 때 아래와 같은 내용들을 제대로 알지 못하고 작성하였다.
처음에 위의 코드를 작성하였을 때 bulk delete 후 flush와 clear 처리를 하지 않았다. 이러한 이유 때문인지는 모르겠지만 bulk delete는 모두 동작하는데 timetableRepository.delete(timetable)는 동작하지 않는 기현상이 발생하였다. 하지만 bulk delete 후 flush와 clear 처리를 한 후 timetableRepository.delete(timetable) 제대로 동작하게 되었다. 문제는 이유를 모르겠다.