QueryDSL 중급

이동건 (불꽃냥펀치)·2025년 3월 16일

프로젝션과 결과 반환

프로젝션 대상이 하나

List<String> result = queryFactory
					.select(member.username)
                    .from(member)
                    .fetch();
  • 프로젝션 대상이 하나면 타입을 명확히 지정할 수 있음
  • 프로젝션 대상이 둘 이상이면 튜플이나 DTO로 조회

튜플 조회

  • 프로젝션 대상이 둘 이상일 때 사용
List<Tuple> result= queryFactory
.select(member.username,member.age)
.from(member)
.fetch();



프로젝션 결과 DTO 조회

MemberDto

@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 조회 코드

List<MemberDto> result = em.createQuery(
"select new study.querydsl.dto.MemberDto(m.username,m.age)" +
"from Member m",MemberDto.class)
.getresultList();
  • 순수 JPA에서는 DTO를 조회할때 new 명령어를 사용해야함
  • DTO의 패키지명과 생성자 방식만 지원해서 더러움

Querydsl 빈 생성

  • 프로퍼티 접근
  • 필드 직접 접근
  • 생성자 사용

프로퍼티 접근 setter

List<MemberDto> result =queryFactory.
select(Projections.bean(MemberDto.class,member.username,member.age))
.from(member)
.fetch();

필드 직접 접근

List<MemberDto> result =queryFactory
.select(Projections.fields(MemberDto.class,member.suername,member.age))
.from(member)
.fetch();

별칭이 다를 때

@Data
public class UserDto {
    private String name;
    private int age;

    public UserDto() {
    }

    public UserDto(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 코드
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();
  • ExpressionUtils.as(source,alias): 필드나 서브 쿼리에 별칭 적용
  • username.as("memberName"): 필드에 별칭 적용

생성자 사용

List<MemberDto> result =queryFactory
.select(Projections.constructor(MemberDto.class,member.username,member.age))
.from(member)
.fetch();

@QueryProjection

@Data
public class MemberDto {
    private String username;
    private int age;

    public MemberDto() {
    }
    @QueryProjection
    public MemberDto(String username, int age) {
        this.username = username;
        this.age = age;
    }
}
  • 이때 QMemberDto 생성 확인

@QueryProjection 활용

List<MemberDto> result =queryFactory
.select(new QMemberDto(member.username,member.age))
.from(member)
.fetch();



동적쿼리

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

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

    private List<Member> searchMember1(String usernameParam, Integer ageParam) {
        BooleanBuilder builder = new BooleanBuilder();
        if(usernameParam!=null){
            builder.and(member.username.eq(usernameParam));
        }
        if(ageParam!=null){
            builder.and(member.age.eq(ageParam));
        }
        
        return queryFactory.selectFrom(member).where(builder).fetch();
    }
  • BooleanBuilder로 동적쿼리 설정

where 다중 파라미터 사용

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

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

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

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

    private BooleanExpression usernameEq(String usernameParam) {
        return usernameParam==null ? null :  member.username.eq(usernameParam);
    }
  • where 조건에 null값은 무시
  • 메서드를 다른 쿼리에서도 재활용 가능
  • 쿼리자체의 가독성이 높아짐

조합 가능

private BooleanExpression allEq(String usernameParam, Integer ageParam) {
        return usernameEq(usernameParam).and(ageEq(ageParam));
    }



수정/삭제 벌크 연산

쿼리한번으로 대량 데이터 수정

long count = queryFactory.update(member).set(member.username,"비회원")
                        .where(member.age.lt(28)).execute();

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

기존 숫자에 더하기/곱하기

long count =queryFactory.update(member).set(member.age,member.age.add(1)).execute();
long count2 =queryFactory.update(member).set(member.age,member.age.multiply(2)).execute();

대용량 삭제

long cnt= queryFactory.delete(member).where(member.age.gt(18)).execute();

출처: https://www.inflearn.com/course/querydsl-%EC%8B%A4%EC%A0%84

profile
자바를 사랑합니다

0개의 댓글