FireDrago

[QueryDSL] 중급 문법 본문

카테고리 없음

[QueryDSL] 중급 문법

화이용 2024. 5. 3. 14:59

프로젝션(select 대상) 지정 결과 반환 - DTO 조회

@Data
public class MemberDto {

    private String username;
    private int age;

    public MemberDto() {}

    public MemberDto(String username, int age) {
        this.username = username;
        this.age = age;
    }
}

DTO 코드를 만들어준다. select 에서 DTO로 결과를 받아보자

 

 Querydsl 빈 생성(Bean population)

1. 프로퍼티 접근 : Projections.bean(클래스, 파라미터)

@Test
void findDtoBySetter() {
    List<MemberDto> result = queryFactory
    //       프로퍼티 접근 setter 사용
            .select(Projections.bean(MemberDto.class,
                    member.username,
                    member.age))
            .from(member)
            .fetch();

    for (MemberDto memberDto : result) {
        System.out.println("memberDto = " + memberDto);
    }
}

 

2. 필드 직접 접근 : Projections.fields(클래스, 파라미터)

@Test
void findDtoByField() {
    List<MemberDto> result = queryFactory
    //      필드 직접 접근
            .select(Projections.fields(MemberDto.class,
                    member.username,
                    member.age))
            .from(member)
            .fetch();

    for (MemberDto memberDto : result) {
        System.out.println("memberDto = " + memberDto);
    }
}

 

3. 생성자 사용 : Projections.constructor(클래스, 파라미터)

@Test
void findDtoByConstructor() {
    List<MemberDto> result = queryFactory
    //      생성자 사용
            .select(Projections.constructor(MemberDto.class,
                    member.username,
                    member.age))
            .from(member)
            .fetch();

    for (MemberDto memberDto : result) {
        System.out.println("memberDto = " + memberDto);
    }
}

 

※ 서브쿼리를 사용하여 별칭이 다를때 

@Test
void findUserDto() {
//  QMember 별칭 생성
    QMember memberSub = new QMember("memberSub");

    List<UserDto> fetch = queryFactory
            .select(Projections.fields(UserDto.class,
                    member.username.as("name"),
                    ExpressionUtils.as(
                            JPAExpressions
                                    .select(memberSub.age.max())
                                    .from(memberSub), "age")
                    )
            ).from(member)
            .fetch();
}

 

 

4. @QueryProjection

@Data
public class MemberTeamDto {
    private Long memberId;
    private String username;
    private int age;
    private Long teamId;
    private String teamName;

    @QueryProjection
    public MemberTeamDto(Long memberId, String username,
                         int age, Long teamId, String teamName) {
        this.memberId = memberId;
        this.username = username;
        this.age = age;
        this.teamId = teamId;
        this.teamName = teamName;
    }
}

 

MemberTeamDto@QueryProjection  을 생성자에 붙여준다.

gradle => others => compile java 클릭하여 QMember 객체를 생성한다.

@Test
void findDtoByQueryProjection() {
    List<MemberDto> result = queryFactory
            // 컴파일시 타입 체크할 수 있다.
            // new QMemberDto() 생성한다.
            .select(new QMemberDto(member.username, member.age))
            .from(member)
            .fetch();

    for (MemberDto memberDto : result) {
        System.out.println("memberDto = " + memberDto);
    }
}

 

 

 

동적쿼리 - BooleanBuilder 사용

@Test
void 동적쿼리_BooleanBuilder() throws Exception {
    String username = "member1";
    Integer ageParam = 10;

    List<Member> result = searchMember1(username, ageParam);
    assertThat(result.size()).isEqualTo(1);
}

private List<Member> searchMember1(String userCond, Integer ageCond) {
    BooleanBuilder builder = new BooleanBuilder();
    if (userCond != null) {
        builder.and(member.username.eq(userCond));
    }
    if (ageCond != null) {
        builder.and(member.age.eq(ageCond));
    }
    return queryFactory
            .selectFrom(member)
            .where(builder)
            .fetch();
}

 

동적쿼리 - Where 다중 파라미터 사용

@Test
void 동적쿼리_WhereParam() throws Exception {
    String username = "member1";
    Integer ageParam = 10;

    List<Member> result = searchMember2(username, ageParam);
    assertThat(result.size()).isEqualTo(1);
}

private List<Member> searchMember2(String userCond, Integer ageCond) {
    return queryFactory
            .selectFrom(member)
            .where(allEq(userCond, ageCond))
            .fetch();
}

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

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

// 메서드를 다른 쿼리에서도 재활용 할 수 있다. (장점)
private BooleanExpression allEq(String usernameCond, Integer ageCond) {
    return usernameEq(usernameCond).and(ageEq(ageCond));
}

- where 조건에 null 값은 무시된다.

- 쿼리 자체의 가독성이 높아진다.

 

 

수정, 삭제 벌크연산

@Test
void bulkUpdate() {
    queryFactory
            .update(member)
            .set(member.username, "비회원")
            .where(member.age.lt(20))
            .execute();

    // 영속성 컨텍스트 초기화 반드시 해주자
    em.flush();
    em.clear();
}
@Test
void bulkDelete() {
    queryFactory
            .delete(member)
            .where(member.age.gt(18))
            .execute();

    em.flush();
    em.clear();
}

영속성 컨텍스트에 있는 엔티티를 무시하고 실행되기 때문에 배치 쿼리를 실행하고 나면

영속성 컨텍스트를 초기화 하는 것이 안전하다.

 

 

SQL function 호출하기

@Test
void sqlFunction() {
    List<String> result = queryFactory
            .select(Expressions.stringTemplate("function('replace', {0}, {1}, {2})",
                    member.username, "member", "M"))
            .from(member)
            .fetch();

    for (String s : result) {
        System.out.println("s = " + s);
    }
}
@Test
    void sqlFunction2() {
        List<String> result = queryFactory
                .select(member.username)
                .from(member)
//                .where(member.username.eq(Expressions.stringTemplate("function('lower', {0})",
//                        member.username)))
                .where(member.username.eq(member.username.lower()))
                .fetch();

        for (String s : result) {
            System.out.println("s = " + s);
        }
    }

lower 같은 ansi 표준 함수들은 querydsl 이 상당부분 내장하고 있다.