2 분 소요


여기에 나오는 기능은 제약이 커서 복잡한 실무 환경에서 사용하기에는 많이 부족하다.


인터페이스 지원

QuerydslPredicateExecutor

QuerydslPredicateExecutor

public interface QuerydslPredicateExecutor<T> {

    Optional<T> findById(Predicate predicate); 
    Iterable<T> findAll(Predicate predicate); 
    long count(Predicate predicate); 
    boolean exists(Predicate predicate);

    // … more functionality omitted.
}

적용

public interface MemberRepository extends JpaRepository<Member, Long>, QuerydslPredicateExecutor<Member> {

}
@Test
void querydslPredicateExecutorTest() {

    ...

    QMember member = QMember.member;
    Iterable result =  memberRepository.findAll(
            member.age.between(10,40)
            .and(member.username.eq("member1"))
    );
}

한계점

  • 조인X (묵시적 조인은 가능하지만 left join이 불가능하다.)
  • 클라이언트가 Querydsl에 의존해야 한다.
    • 서비스 클래스가 Querydsl이라는 구현 기술에 의존해야 한다.
  • 복잡한 실무환경에서 사용하기에는 한계가 명확하다.

[참고] : QuerydslPredicateExecutor는 Pageable, Sort를 모두 지원한다.


Querydsl Web 지원

공식 URL

한계점

  • 단순한 조건만 가능
  • 조건을 커스텀하는 기능이 복잡하고 명시적이지 않음
  • 컨트롤러가 Querydsl에 의존
  • 복잡한 실무환경에서 사용하기에는 한계가 명확


리포지토리 지원

QuerydslRepositorySupport

장점

  • getQuerydsl().applyPagination() 스프링 데이터가 제공하는 페이징을 Querydsl로 편리하게 변환 가능
    • 단! Sort는 오류 발생
  • from()으로 시작
  • EntityManager 제공

한계

  • Querydsl 3.x 버전을 대상으로 만듬
  • Querydsl 4.x에 나온 JPAQueryFactory로 시작할 수 없음
    • select로 시작할 수 없음(from시작)
  • QueryFactory를 제공하지 않음
  • 스프링 데이터 Sort 기능이 정상 동작하지 않음


Querydsl 지원 클래스 직접 만들기

스프링 데이터가 제공하는 QuerydslRepositorySupport가 지닌 한계를 극복하기 위해 직접 Querydsl 지원 클래스를 만들었다.

장점

  • 스프링 데이터가 제공하는 페이징을 편리하게 변환
  • 페이징과 카운트 쿼리 분리 가능
  • 스프링 데이터 Sort 지원
  • select(), selectFrom()으로 시작 가능
  • EntityManager, QueryFactory 제공

Querydsl4RepositorySupport

@Repository
public abstract class Querydsl4RepositorySupport {

    private final Class domainClass;
    private Querydsl querydsl;
    private EntityManager entityManager;
    private JPAQueryFactory queryFactory;

    ...

    protected <T> JPAQuery<T> select(Expression<T> expr) {
        return getQueryFactory().select(expr);
    }

    protected <T> JPAQuery<T> selectFrom(EntityPath<T> from) {
        return getQueryFactory().selectFrom(from);
    }

    protected <T> Page<T> applyPagination(Pageable pageable, Function<JPAQueryFactory, JPAQuery> contentQuery) {
        JPAQuery jpaQuery = contentQuery.apply(getQueryFactory());
        List<T> content = getQuerydsl().applyPagination(pageable, jpaQuery).fetch();
        return PageableExecutionUtils.getPage(content, pageable, jpaQuery::fetchCount);
    }

    protected <T> Page<T> applyPagination(Pageable pageable, Function<JPAQueryFactory, JPAQuery> contentQuery, Function<JPAQueryFactory, JPAQuery> countQuery) {
        JPAQuery jpaContentQuery = contentQuery.apply(getQueryFactory());
        List<T> content = getQuerydsl().applyPagination(pageable, jpaContentQuery).fetch();
        JPAQuery countResult = countQuery.apply(getQueryFactory());
        return PageableExecutionUtils.getPage(content, pageable, countResult::fetchCount);
    }
}

사용 코드

@Repository
public class MemberTestRepository extends Querydsl4RepositorySupport {

    public MemberTestRepository() {
        super(Member.class);
    }

    public List<Member> basicSelect() {
        return select(member)
                .from(member)
                .fetch();
    }

    public Page<Member> searchPageByApplyPage(MemberSearchCondition condition, Pageable pageable) {

        JPAQuery<Member> query = selectFrom(member)
                .leftJoin(member.team, team)
                .where(
                    usernameEq(condition.getUsername()),
                    teamNameEq(condition.getTeamName()),
                    ageGoe(condition.getAgeGoe()),
                    ageLoe(condition.getAgeLoe()));

        List<Member> content = getQuerydsl().applyPagination(pageable, query).fetch();

        return PageableExecutionUtils.getPage(content, pageable, query::fetchCount);
    }

    public Page<Member> applyPagination(MemberSearchCondition condition, Pageable pageable) {

        return applyPagination(pageable, contentQuery -> contentQuery
                .selectFrom(member)
                .leftJoin(member.team, team)
                .where(
                    usernameEq(condition.getUsername()),
                    teamNameEq(condition.getTeamName()),
                    ageGoe(condition.getAgeGoe()),
                    ageLoe(condition.getAgeLoe())));
    }

    public Page<Member> applyPagination2(MemberSearchCondition condition, Pageable pageable) {
        return applyPagination(pageable, contentQuery -> contentQuery
                .selectFrom(member)
                .leftJoin(member.team, team)
                .where(
                    usernameEq(condition.getUsername()),
                    teamNameEq(condition.getTeamName()),
                    ageGoe(condition.getAgeGoe()),
                    ageLoe(condition.getAgeLoe()))),
                countQuery -> countQuery
                        .selectFrom(member)
                        .leftJoin(member.team, team)
                        .where(
                            usernameEq(condition.getUsername()),
                            teamNameEq(condition.getTeamName()),
                            ageGoe(condition.getAgeGoe()),
                            ageLoe(condition.getAgeLoe()));
    }

    //usernameEq, TeamNameEq..
}


<출처 : 인프런 - 실전! Querydsl(김영한)>

댓글남기기