이번 포스팅은 QueryDSL 이용해서 목록 페이징 + 검색을 위한 데이터 조회 방법에 대해 다루고자 한다.
queryDSL 사용 설정은 다른 포스팅에서 다룰 예정이다. 그럼 시작해보자고~!
예로 사용자 목록 검색하는 코드를 queryDSL으로 짜보자!
//SearchDto
@Getter
@Setter
public class SearchDto {
private String searchType;
private String searchText;
}
//UserDto
@Getter
@Setter
@RequiredArgsConstructor
public class UserDto {
@NotNull
private String id;
@NotNull
private String name;
@NotNull
private String tel;
private int level;
}
springJPA에서 제공하는 함수를 커스텀하거나 직접 쿼리를 짜고 싶을 때 이 방법을 이용한다.
//CustomRepository.java (interface)
public interface CustomRepository {
Page<SearchResultDto> searchPageList(SearchDto search, Pageable pageable);
}
//CustomRepositoryImpl.java
@Repository
@RequiredArgsConstructor
public java CustomRepositoryImpl implements CustomRepository{
QUserEntity user = new QUserEntity();
private final JPAQueryFactory queryFactory;
@Override
Page<SearchResultDto> searchPageList(SearchDto search, Pageable pageable){
List<SearchResultDto> searchList = queryFactory
.select(Projection.contruct(UserDto.class,
user.id,
user.name,
user.tel)
.from(user)
.where(searchCondition(search)
.orderBy(getOrderSpecifier(pageable, UserEntity.class))
.orderBy(settingOrder(pageable)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
Long count = queryFactory
.select(user.count())
.from(user)
.where(searchCondition(search)
.fetchOne();
return PageImpl<>(searchList, pageable, count);
}
private BooleanBuilder searchCondition(Search search) {
BooleanBuilder booleanBuilder = null;
if(search != null) {
if(!search.getSearchType().isEmpty() && !search.getSearchText().isEmpty() ) {
switch (search.getSearchType()) {
case "id":
booleanBuilder = new BooleanBuilder(user.id.contains(searchText));
break;
case "name":
booleanBuilder = new BooleanBuilder(user.name.contains(searchText));
break;
case "tel":
booleanBuilder = new BooleanBuilder(user.tel.contains(searchText));
break;
}
}
}
return booleanBuilder;
}
public OrderSpecifier getOrderSpecifier(Pageable paging, Class entityClass) {
Sort.Order[] orderList = paging.getSort().stream().toArray(Sort.Order[]::new);
for(Sort.Order order : orderList) {
Order direction = order.getDirection().isAscending() ? Order.ASC : Order.DESC;
PathBuilder pathBuilder = new PathBuilder(entityClass, order.getProperty());
return new OrderSpecifier(direction, pathBuilder.get(order.getProperty()));
}
return null;
}
}
getOrderSpecifier 함수는 여러 엔터티에서 사용할 수 있도록 짜봤다. 지금 예제에서는 엔터티 하나여서 한 CustomRepositoryImpl에서 해당 함수를 선언해서 사용해도 되지만 엔터티가 여러개인 경우 매번 같은 함수를 짜지 않도록하고자 했다.
검색 목록 기능을 이용하려면 CustomRepository 인터페이스를 UserRepository에서 상속받은 후 service단에서 호출하여 사용하면 된다.
참고자료
https://jddng.tistory.com/345
https://ttl-blog.tistory.com/216
https://kkambi.tistory.com/193