백엔드/Spring

[Spring] Querydsl DTO & 동적 쿼리

kwang2134 2024. 9. 26. 15:17
728x90
반응형
728x90

Querydsl DTO 조회

  • Querydsl 사용하여 결과로 DTO를 반환할 때 사용하는 방법

프로퍼티 접근

  • JPAQueryFactory를 사용하여 쿼리를 실행한 후 select 절에서 DTO의 getter 메서드를 호출
  • Projections.bean()를 select절에 사용하여 Getter 호출을 위한 프로퍼티 작성
  • DTO에 Getter와 Setter가 존재해야 함 
  • 필드명과 Getter, Setter 명이 같아야 함 
//프로퍼티 접근
List<UserDTO> users = queryFactory
    .select(Projections.bean(UserDTO.class, 
        QUser.user.name, 
        QUser.user.age))
    .from(QUser.user)
    .fetch();

 

필드 접근

  • DTO의 필드에 직접 접근하여 값을 설정하는 방식
  • Projections.fields()를 select절에 사용하여 DTO 필드에 값을 매핑
  • 필드명이 쿼리에 사용되는 별칭과 같아야 함
  • 필드명이 다르다면 as()를 사용해 별칭을 지정
//필드 접근
List<UserDTO> users = queryFactory
    .select(Projections.fields(UserDTO.class, 
        QUser.user.name, 
        QUser.user.age))
    .from(QUser.user)
    .fetch();
    
//별칭 사용
List<UserDTO> users = queryFactory
    .select(Projections.fields(UserDTO.class, 
        QUser.user.name.as("username"), 
        QUser.user.age))
    .from(QUser.user)
    .fetch();

 

생성자 접근

  • DTO의 생성자를 통해 값을 설정하는 방식
  • Projections.constructor()를 select절에 사용하여 DTO 생성자를 호출하여 객체를 생성 
  • 생성자가 필드의 순서와 타입에 맞춰 정의되어 있어야 함 -> 파라미터 이름이 다르거나 순서가 틀리면 매핑이 제대로 이루어지지 않을 수 있음
//생성자 접근
List<UserDTO> users = queryFactory
    .select(Projections.constructor(UserDTO.class, 
        QUser.user.name, 
        QUser.user.age))
    .from(QUser.user)
    .fetch();

 

QueryProjection - DTO Q-Type 생성

  • DTO에 직접 Querydsl의 Q타입을 생성하여 사용하는 방법
  • DTO 클래스 생성자에 @QueryProjection 어노테이션을 사용
  • select 절에 new 명령어를 통한 Q타입 DTO 객체를 생성하여 사용
  • 장점: 컴파일 에러를 방지할 수 있음
  • 단점: 엔티티가 Querydsl의 종속성을 가지게 됨
// QueryDSL
QUser user = QUser.user;
List<UserDTO> users = queryFactory
    .select(new QUserDTO(user.name, user.age))
    .from(user)
    .fetch();

동적 쿼리

BooleanBuilder

  • 여러 조건을 조합할 수 있도록 도와주는 유틸리티 클래스
  • 여러 조건을 BooleanBuilder 객체에 정의해두고 where() 절에 BooleanBuilder 객체를 넣어 사용
  • 해당 조건이 참인 경우만 조건에 추가 됨

주요 메서드

  • and(): 조건을 AND로 결합
  • or(): 조건을 OR로 결합
  • reset(): 현재 조건을 초기화
  • isEmpty(): 현재 조건이 비어 있는지 확인
  • getValue(): 현재 조건을 BooleanExpression으로 반환
  • add(): 기존의 조건을 추가
BooleanBuilder builder = new BooleanBuilder();

//and조건 - 유저 이름이 park이고 나이가 30 이상인 조건
builder.and(QUser.user.name.eq("park")).and(QUser.user.age.goe(30));

//or조건 - 유저 이름이 park이거나 나이가 30인 조건
builder.or(QUser.user.name.eq("park")).or(QUser.user.age.eq(30));

//AND로 조건을 추가 BooleanExpression
builder.add(QUser.user.name.eq("kim"));

//조건이 비어있는지 검사
builder.isEmpty()

//모든 조건 초기화
builder.reset(); 

//현재 조건을 BooleanExpression으로 반환
builder.getValue();

//사용 예시
public List<UserDTO> findUsers(String name, Integer age) {
    QUser user = QUser.user;
    BooleanBuilder builder = new BooleanBuilder();

    if (name != null && !name.isEmpty()) {
        builder.and(user.name.eq(name));
    }

    if (age != null) {
        builder.and(user.age.eq(age));
    }

    return queryFactory
        .select(Projections.bean(UserDTO.class, user.name, user.age))
        .from(user)
        .where(builder)
        .fetch();
}

 

Where 다중 파라미터 사용

  • where절의 다중 파라미터를 사용하여 검색 조건을 추가
  • 기존 and 조건으로 추가 
  • 메서드를 통해 사용 시 재사용 가능
//null 값일 경우 해당 조건의 구문이 생성되지 않음
public List<UserDTO> findUsers(String name, Integer age) {
    QUser user = QUser.user;

    return queryFactory
        .select(Projections.bean(UserDTO.class, user.name, user.age))
        .from(user)
        .where(name != null ? user.name.eq(name) : null, age != null ? user.age.eq(age) : null)
        .fetch();
}

//메서드를 뽑아서 처리
public List<UserDTO> findUsers(String name, Integer age) {
    QUser user = QUser.user;

    return queryFactory
        .select(Projections.bean(UserDTO.class, user.name, user.age))
        .from(user)
        .where(nameEq(name), ageEq(age))
        .fetch();
}

private BooleanExpression nameEq(String nameCond) {
    return nameCond != null ? QUser.user.name.eq(nameCond) : null;
}

private BooleanExpression ageEq(Integer ageCond) {
    return ageCond != null ? QUser.user.age.eq(ageCond) : null;
}

 

SQL function 호출

  • JPA와 같이 Dialect에 등록된 내용만 호출 가능
  • Expressions.stringTemplate()을 사용하여 사용
  • ansi 표준 함수들은 querydsl 이 기본적으로 상당 부분 내장 함수로 가지고 있음
QUser user = QUser.user;

//Expressions.stringTemplate 사용
queryFactory.select(user.name)
 .from(user)
 .where(user.name.eq(Expressions.stringTemplate("function('lower', {0})", user.name)))

//내장 함수로 처리
 .where(user.name.eq(user.name.lower()))

 

서브 쿼리

  • JPAExpression을 사용해 서브 쿼리 가능
//서브 퀄;
public List<Member> findMembersWithSubquery() {
    QUser userSub = new QUser("userSub");
    QUser user = QUser.user;
    
    return queryFactory
        .selectFrom(user)
        .where(user.age.eq(
            JPAExpressions
                .select(userSub.age.max())
                .from(userSub)
        ))
        .fetch();
}

실전! Querydsl 강의 | 김영한 - 인프런 (inflearn.com) 강의 내용 참고

 

 

728x90

'백엔드 > Spring' 카테고리의 다른 글

[Spring] Jar & SpringBoot Jar  (0) 2024.09.28
[Spring] 스프링 부트  (2) 2024.09.27
[Spring] Querydsl  (1) 2024.09.25
[Spring] Spring Data Jpa 확장 기능  (0) 2024.09.24
[Spring] 쿼리 메서드  (0) 2024.09.23