개발놀이터

Querydsl 중급문법 본문

JPA/QueryDSL

Querydsl 중급문법

마늘냄새폴폴 2021. 10. 1. 21:22

*프로젝션과 결과 반환
프로젝션 : select 대상 지정

-프로젝션 대상이 하나일 때 (단일 타입)

List<String> result = queryFactory.select(member.username).from(member).fetch();



1. 프로젝션 대상이 하나면 타입을 명확하게 지정할 수 있음
2. 프로젝션 대상이 둘 이상이면 튜플이나 DTO로 조회

-프로젝션 대상이 둘 이상일 때 (튜플 사용)

List<Tuple> result = queryFactory.select(member.username, member.age).from(member).fetch();
-사용방법
for (Tuple tuple : result) {
    String username = tuple.get(member.username);
    Integer age = tuple.get(member.age);
}


튜플을 사용할 때는 repository레벨에서만 사용하고 service, controller레벨에선 사용하지 않는 것이 좋다.

-프로젝션 대상이 둘 이상일 때 (DTO 조회)
1. 프로퍼티접근 (Setter 이용)

queryFactory
    .select(Projection.bean(MemberDto.class, member.username, member.age))
    .from(member)
    .fetch();



2. 필드접근

queryFactory
    .select(Projection.fields(MemberDto.class, member.username, member.age))
    .from(member)
    .fetch();



3. 생성자사용

queryFactory
    .select(Projection.constructor(MemberDto.class, member.username, member.age))
    .from(member)
    .fetch();



cf)QueryProjection 사용
DTO의 생성자 부분에 @QueryProjection을 달아주고 compileQuerydsl을 실행 하면 Q타입의 DTO가 생긴다. 이후에

queryFactory.select(new QMemberDto(member.username, member.age)).from(member).fetch();

이렇게 사용하면 된다. 

QueryProjection의 장단점
장점 : 생성자를 생성할 때 DTO에서의 데이터들을 가지고 생성하는 것이기 때문에 실수로 더 많이 혹은 더 적게 인자값을 넘겨주면 컴파일단계에서 오류가 발생한다. 그러므로 굉장히 안전한 방법이다.
단점 : DTO가 QueryDSL에 의존성을 가지고 종속적으로 바뀐다. 또한 DTO를 Q타입으로 또 하나 생성해야 한다는 단점이 있다. 


*동적 쿼리
1. BooleanBuilder 사용

@Test
    public void dynamicQuery_BooleanBuilder() {
        String usernameParam = "member1";
        Integer ageParam = null;

        List<Member> result = searchMember1(usernameParam, ageParam);

        Assertions.assertThat(result.size()).isEqualTo(1);
    }

    private List<Member> searchMember1(String usernameCond, Integer ageCond) {
        BooleanBuilder builder = new BooleanBuilder();

//new BooleanBuilder(member.username.eq("member1")) 이렇게 기본값을 지정할 수도 있다. 
        if (usernameCond != null) {
            builder.and(member.username.eq(usernameCond));
        }

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

        return queryFactory
                .selectFrom(member)
                .where(builder)
                .fetch();
    }



2. where 다중파라미터 사용

@Test
    public void dynamicQuery_WhereParam() {
        String usernameParam = "member1";
        Integer ageParam = 10;

        List<Member> result = searchMember2(usernameParam, ageParam);

        Assertions.assertThat(result.size()).isEqualTo(1);
    }

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

    private BooleanExpression usernameEq(String usernameCond) { //나중에 조립하기 위해서는 BooleanExpression타입으로 리턴타입을 받아야 한다. 
        return usernameCond != null ? member.username.eq(usernameCond) : null;
    }

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



where 다중 파라미터사용의 특징
1. where 조건에 null값은 무시된다.
2. 메서드를 다른 쿼리에서도 재활용 할 수 있다.
3. 쿼리 자체의 가독성이 높아진다.


*벌크성 쿼리
벌크성 쿼리란? JPA의 더티체킹(변경감지)을 사용한 데이터 수정은 각각 일어나기 때문에 쿼리가 하나씩 나가는 단점이 있다. 벌크성 쿼리는 일반 SQL문 처럼 한번에 여러개의 데이터를 수정할 수 있게 해주는 기능이다. 다만 벌크성 쿼리는 JPA의 영속성컨텍스트와 전혀 관계없이 동작하기 때문에 데이터가 일치하지 않는 경우가 생길 수 있다. 이 때문에 벌크성 쿼리를 실행한 후에는 영속성컨텍스트를 완전히 초기화 시켜주는 것이 관례이다. 

queryFactory
    .update(member)
    .set(member.username, "memberA")
    .where(member.username.eq("member1"))
    .execute();

'JPA > QueryDSL' 카테고리의 다른 글

스프링 부트 5.0 Querydsl 설정 변경  (0) 2023.05.30
QueryDSL 중급문법  (0) 2022.02.07
QueryDSL 기본문법  (0) 2022.02.07
Querydsl 실무활용  (0) 2021.10.01
Querydsl 기본문법  (0) 2021.10.01