[QueryDSL] 동적 쿼리 작성기 + 페이징

울상냥·2023년 4월 10일
0

SpringBoot

목록 보기
5/11

현재 진행중인 프로젝트는 두 가지 검색 조건이 있는데

  • 기간별 검색
  • 직원별 검색

이 조건들이 만드는 경우의 수는 4가지로 기존 JPA 쿼리메소드 4가지를 작성해줘야하고 service단에서도 분기를 작성해 줘야하는 문제가 생겼다.
지금은 조건이 2가지밖에 없어 작성이 어렵진 않겠지만 조건이 더 많아진다면..? 😐

하여 QueryDSL을 사용하여 동적 쿼리를 작성해보기로 했다.


QueryDSL

QueryDsl은 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해주는 오픈소스 프레임워크이다.

장점

  • 코드로 쿼리를 작성하여 컴파일 시점에 오류 검출 가능
  • 동적인 쿼리 작성
  • 네이티브 쿼리보다 좋은 가독성

등등 적용시에 공부해야할 점은 조금 있었지만 적용 후 꽤나 깔끔하고 만족스러웠다


interface 작성

Querydsl을 사용하는 여러가지 방법이 있었는데, 나는 그중에서 interface를 작성해 구현하고 repository에서 상속받아 사용하는 방식을 택했다.
이유는 entity당 하나의 repository만 의존성으로 받아 사용하고 싶었기 때문에

public interface QueryDSLRepository {

    Page<Work> findByUserAndPeriodAndEmployee(User user, LocalDate dateFrom, LocalDate dateTo, Long employeeId, Pageable pageable);
}

인터페이스를 작성해주었다.

Impl 작성

@Repository
@RequiredArgsConstructor
public class WorkRepositoryImpl implements QueryDSLRepository {

    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public Page<Work> findByUserAndPeriodAndEmployee(User user, LocalDate dateFrom, LocalDate dateTo, Long employeeId, Pageable pageable) {

        List<Work> content = jpaQueryFactory
                .selectFrom(work)
                .where(userEq(user), dateBetween(dateFrom, dateTo), employeeIdEq(employeeId))
                .orderBy(work.date.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        JPAQuery<Long> count = jpaQueryFactory
                .select(work.count())
                .from(work)
                .where(userEq(user), dateBetween(dateFrom, dateTo), employeeIdEq(employeeId));

        return PageableExecutionUtils.getPage(content, pageable, count::fetchOne);
    }

    private BooleanExpression userEq(User user) {
        return work.employee.user.eq(user);
    }

    private BooleanExpression dateBetween(LocalDate dateFrom, LocalDate dateTo) {

        if (dateFrom == null & dateTo == null) {
            return null;
        }

        return work.date.between(dateFrom, dateTo);
    }

    private BooleanExpression employeeIdEq(Long employeeId) {
        if(employeeId == null) {
            return null;
        }
        return work.employee.id.eq(employeeId);
    }
}

where절의 동적쿼리는 BooleanExpression 을 사용해 작성하는것이 재사용성, 가독성, 안정성 면에서 좋다고 한다.

fetchCount()fetchResult() 는 deprecated 되어 사용할 수 없다.

Count쿼리 최적화를 위해서 count쿼리를 별도로 JPAQuery를 작성하고
반환 객체로 PageImpl을 사용하지 않고 PageableExecutionUtils를 사용하였다.

PageableExecutionUtils.getPage(content, pageable, count::fetchOne) 시에는 count쿼리를 필요한 경우에만 날리기 때문에 최적화가 가능하다!!

profile
안되면 되게하라

0개의 댓글