[실전! Querydsl] 스프링 데이터 JPA가 제공하는 Querydsl 기능

2022. 11. 25. 18:53· course/inflearn

아래 세가지 지원 기능은 제약이 커서 복잡한 실무 환경에서의 사용은 선택적으로!

 

1. 인터페이스 지원 - QuerydslPredicateExecutor

  • Pageable, Sort 지원
// 리포지토리에 적용
public interface MemberRepository extends JpaRepository<Member, Long>, CustomMemberRepository, QuerydslPredicateExecutor<Member> {
	...
}

// 서비스에서 사용
Iterable result = memberRepository.findAll(
        member.age.between(10, 40)
        .and(member.username.eq("1")));
  • 한계
    • 외부 조인 불가능 (묵시적 조인 가능)
    • 클라이언트가 Querydsl 구현기술에 의존

 

2. Querydsl Web 지원

  • 한계
    • 기본적으로는 eq, contains, in만 가능
    • 기능 추가를 위한 커스텀이 복잡하고 명시적이지 않음
    • 컨트롤러가 Querydsl에 의존

 

3. 리포지토리 지원 - QuerydslRepositorySupport

// 추상 클래스 상속
public class MemberRepositoryImpl extends QuerydslRepositorySupport implements CustomMemberRepository {

    //private final JPAQueryFactory queryFactory;

    public MemberRepositoryImpl(EntityManager em) {
        super(Member.class);
        //this.queryFactory = new JPAQueryFactory(em);
    }
    
    ...
}

// 사용
JPQLQuery<MemberTeamDto> jpaQuery = from(member)
        .leftJoin(member.team, team)
        .where(
                usernameEq(condition.getUsername()),
                teamNameEq(condition.getTeamName()),
                ageGoe(condition.getAgeGoe()),
                ageLoe(condition.getAgeLoe()))
        .select(new QMemberTeamDto(
                member.id,
                member.username,
                member.age,
                team.id,
                team.name
        ));


JPQLQuery<MemberTeamDto> resultQuery = getQuerydsl().applyPagination(pageable, jpaQuery);
resultQuery.fetch();
  • 장점
    • EntityManager 제공
    • getQuerydsl().applyPagination() 으로 스프링 데이터가 제공하는 페이징을 Querydsl로 편리하게 변환 가능
      • 쿼리에 offset, limit 없애도 됨
  • 한계
    • Querydsl 3.X 버전을 대상으로 만들어서 Querydsl 4.X에 나온 JPAQueryFactory로 시작 불가능
      • 쿼리가 from 부터 시작
    • 스프링 데이터 Sort 기능이 정상 작동하지 않음

 

Querydsl 지원 클래스 직접 만들기

  • QuerydslRepositorySupport의 한계를 보완하는 Querydsl 지원 클래스 직접 생성
    • Java의 Function을 활용해 람다 함수를 파라미터로 받아 쿼리 수행
    • 장점
      • 스프링 데이터 페이징 기능을 편리하게 변환
      • 페이징과 카운트 쿼리 분리 가능
      • 스프링 데이터 Sort 정상 지원
      • select(), selectFrom()으로 시작 가능
      • EntityManager, JPAQueryFactory 제공
/**
 * Querydsl 4.x 버전에 맞춘 Querydsl 지원 라이브러리
 *
 * @author Younghan Kim
 * @see org.springframework.data.jpa.repository.support.QuerydslRepositorySupport
 */
@Repository
public abstract class Querydsl4RepositorySupport {
    private final Class domainClass;
    private Querydsl querydsl;
    private EntityManager entityManager;
    private JPAQueryFactory queryFactory;

    public Querydsl4RepositorySupport(Class<?> domainClass) {
        Assert.notNull(domainClass, "Domain class must not be null!");
        this.domainClass = domainClass;
    }

    @Autowired
    public void setEntityManager(EntityManager entityManager) {
        Assert.notNull(entityManager, "EntityManager must not be null!");
        JpaEntityInformation entityInformation = JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager);
        SimpleEntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;
        EntityPath path = resolver.createPath(entityInformation.getJavaType());
        this.entityManager = entityManager;
        this.querydsl = new Querydsl(entityManager, new PathBuilder<>(path.getType(), path.getMetadata()));
        this.queryFactory = new JPAQueryFactory(entityManager);
    }

    @PostConstruct
    public void validate() {
        Assert.notNull(entityManager, "EntityManager must not be null!");
        Assert.notNull(querydsl, "Querydsl must not be null!");
        Assert.notNull(queryFactory, "QueryFactory must not be null!");
    }

    protected JPAQueryFactory getQueryFactory() {
        return queryFactory;
    }

    protected Querydsl getQuerydsl() {
        return querydsl;
    }

    protected EntityManager getEntityManager() {
        return entityManager;
    }

    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 List<Member> basicSelectFrom() {
        return selectFrom(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> applySimplePagination(MemberSearchCondition condition, Pageable pageable) {
    //    return applyPagination(pageable, query -> query
    //            .selectFrom(member)
    //            .leftJoin(member.team, team)
    //            .where(usernameEq(condition.getUsername()),
    //                    teamNameEq(condition.getTeamName()),
    //                    ageGoe(condition.getAgeGoe()),
    //                    ageLoe(condition.getAgeLoe())));
    //}

	// 커스텀 클래스 적용 코드
    public Page<Member> applyComplexPagination(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()))
        );
    }

    private BooleanExpression usernameEq(String username) {
        return hasText(username) ? member.username.eq(username) : null;
    }

    private BooleanExpression teamNameEq(String teamName) {
        return hasText(teamName) ? team.name.eq(teamName) : null;
    }

    private BooleanExpression ageGoe(Integer ageGoe) {
        return ageGoe != null ? member.age.goe(ageGoe) : null;
    }

    private BooleanExpression ageLoe(Integer ageLoe) {
        return ageLoe != null ? member.age.loe(ageLoe) : null;
    }
}
저작자표시 비영리 동일조건 (새창열림)

'course > inflearn' 카테고리의 다른 글

[실전! Querydsl] 실무 활용 - 스프링 데이터 JPA와 Querydsl  (0) 2022.11.24
[실전! Querydsl] 실무 활용 - 순수 JPA와 Querydsl  (0) 2022.11.23
[실전! Querydsl] 중급 문법  (0) 2022.11.23
[실전! Querydsl] 기본 문법  (1) 2022.11.22
[JPA 프로그래밍 기본편] JPQL - 중급 문법  (0) 2022.11.10
'course/inflearn' 카테고리의 다른 글
  • [실전! Querydsl] 실무 활용 - 스프링 데이터 JPA와 Querydsl
  • [실전! Querydsl] 실무 활용 - 순수 JPA와 Querydsl
  • [실전! Querydsl] 중급 문법
  • [실전! Querydsl] 기본 문법
hjkim0502
hjkim0502
개발 일지
hjkim0502
CODELOG
hjkim0502
글쓰기
전체
오늘
어제
  • Codelog (168)
    • course (61)
      • nomadcoder (5)
      • spartacoding (22)
      • inflearn (27)
      • 생활코딩 (7)
    • CS (68)
      • algorithm & data structure (34)
      • OS (26)
      • CA (0)
      • DB (8)
      • Network (0)
    • 코딩테스트 (2)
    • 이노베이션 캠프 (37)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • JPA
  • Memory
  • 카카오
  • JPQL
  • leetcode
  • QueryDSL
  • KOCW
  • Python
  • html
  • db
  • 레벨2
  • Java
  • OS
  • inflearn
  • til
  • SQL
  • 생활코딩
  • css
  • 프로그래머스
  • cs
  • spring
  • 인프런
  • 자바
  • MongoDB
  • ORM
  • 파이썬
  • API
  • JS
  • dfs
  • ajax

최근 댓글

hELLO · Designed By 정상우.v4.2.2
hjkim0502
[실전! Querydsl] 스프링 데이터 JPA가 제공하는 Querydsl 기능
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.