반응형

querydsl을 이용하여 검색 조건을 이용한 동적 쿼리를 짜봤습니다.

조건의 적용은 아래와 같습니다.

1. 페이지 번호와 페이지 당 개수를 지정하여 페이징을 할 수 있다.

2. 각 필드명으로 정렬을 적용할 수 있다.

3. 크게 세 분류의 옵션이 있으며, 각 옵션의 항목들은 중복 선택할 수 있다.

4. 첫 번째 옵션의 한 항목을 체크하면, 날짜 조건을 추가할 수 있다.

1번과 2번은 QuerydslRepositorySupport를 상속하여 applyPagination으로 쉽게 적용할 수 있었습니다.

3번과 4번은,

(체크된 옵션 1 or(

  날짜 조건 항목 체크되면 start <= 입력일 <= end

)) and (체크된 옵션 2 or)

and (체크된 옵션 3 or)

와 같은 형태로 작성하였습니다.

public class TicketRepositoryImpl extends QuerydslRepositorySupport implements CustomTicketRepository {

    private JPAQueryFactory queryFactory;

    public TicketRepositoryImpl(JPAQueryFactory queryFactory) {
        super(Ticket.class);
        this.queryFactory = queryFactory;
    }

    @Override
    public Page<TicketSimpleDto> findAllTicketWithConditions(TicketSearchConditionDto conditionDto, Pageable pageable) {
        JPAQuery<TicketSimpleDto> query = queryFactory
                .select(Projections.constructor(TicketSimpleDto.class,
                        ticket.id, ticket.title, ticket.price, ticket.view, ticket.address,
                        ticket.startDateTime, ticket.endDateTime, ticket.termType, ticket.ticketStatus,
                        ticket.placeType, user.nickname, ticket.createdAt
                )).from(ticket)
                .leftJoin(ticket.writer, user)
                .where(ticketSearchPredicate(conditionDto));

        // count query 분리. leftJoin이므로 조인 미적용
        long count = queryFactory.selectFrom(ticket).where(ticketSearchPredicate(conditionDto)).fetchCount();
        List<TicketSimpleDto> result = getQuerydsl().applyPagination(pageable,
                query).fetch(); // applyPagination으로 pageable에 지정된 페이징 조건 적용
        return new PageImpl<>(result, pageable, count); // Page로 반환
    }

    // where 절에 적용할 predicate
    private BooleanBuilder ticketSearchPredicate(TicketSearchConditionDto conditionDto) {
        return new BooleanBuilder()
                .and(termTypeOrCondition(conditionDto.getTermTypes(), conditionDto.getDateTime())) // 옵션 1
                .and(placeTypeOrCondition(conditionDto.getPlaceTypes())) // 옵션 2
                .and(ticketStatusOrCondition(conditionDto.getTicketStatuses())); // 옵션 3
    }

    private BooleanBuilder dateTimeCondition(LocalDateTime dateTime) {
        return new BooleanBuilder().and(dateTimeLoe(dateTime)).and(dateTimeGoe(dateTime));
    }

    private BooleanExpression dateTimeLoe(LocalDateTime dateTime) {
        return dateTime != null ? ticket.startDateTime.loe(dateTime) : null;
    }

    private BooleanExpression dateTimeGoe(LocalDateTime dateTime) {
        return dateTime != null ? ticket.endDateTime.goe(dateTime) : null;
    }

    private BooleanBuilder termTypeOrCondition(List<TermType> termTypes, LocalDateTime dateTime) {
        BooleanBuilder builder = new BooleanBuilder();
        termTypes.stream().forEach(t -> builder.or(ticket.termType.eq(t))); // 체크된 항목 or로 묶어줌
        // TermType.INPUT이 체크되어 있으면 직접 입력한 날짜 검색 조건도 추가
        return termTypes.contains(TermType.INPUT) ? builder.or(dateTimeCondition(dateTime)) : builder;
    }

    private BooleanBuilder placeTypeOrCondition(List<PlaceType> placeTypes) {
        BooleanBuilder builder = new BooleanBuilder();
        placeTypes.stream().forEach(p -> builder.or(ticket.placeType.eq(p))); // 체크된 항목 or
        return builder;
    }

    private BooleanBuilder ticketStatusOrCondition(List<TicketStatus> ticketStatuses) {
        BooleanBuilder builder = new BooleanBuilder();
        ticketStatuses.stream().forEach(t -> builder.or(ticket.ticketStatus.eq(t))); // 체크된 항목 or
        return builder;
    }

}

오류가 있으면 지적부탁드립니다.

반응형

+ Recent posts