CS/웹개발

QueryDSL

hyunji1109 2025. 4. 6. 21:07

 

 

 

✅ QueryDSL

Java 기반의 타입 안전한 SQL/JPQL 빌더 라이브러리

복잡한 쿼리를 자바 코드로 작성할 수 있도록 도와주는 도구

JPA로 복잡한 쿼리를 작성할 때는 보통 JPQL이나 @Query를 사용 ->
이 방식은 문자열 기반이기 때문에 컴파일 시점에 문법 오류를 잡아주지 못하고 가독성도 떨어짐 ->
QueryDSL은 이를 해결하기 위해 Java 코드로 SQL을 작성

 

 

✅ 장점

  • 타입 안전성
    • 쿼리를 Java 코드로 작성하므로 컴파일 시점에 오류를 잡을 수 있다.
  • 자동 완성
    • IDE의 자동 완성 기능을 사용할 수 있어 생산성이 높다.
  • 복잡한 동적 쿼리 작성
    • 조건문과 결합하여 동적으로 쿼리를 쉽게 만들 수 있다.
  • 재사용성
    • Query 객체를 재활용하기 쉽다.
  • 가독성
    • JPQL보다 훨씬 명확하고 직관적인 쿼리 작성 가능

 

QueryDSL 설정

1) build.gradle

plugins {
    id 'java'
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

dependencies {
    implementation 'com.querydsl:querydsl-jpa'
    annotationProcessor 'com.querydsl:querydsl-apt'
    annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
    annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
}

// QueryDSL Q 클래스 생성 경로 설정
def querydslDir = "$buildDir/generated/querydsl"
sourceSets {
    main.java.srcDirs += querydslDir
}

 

 

2) 엔티티

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String username;
    private int age;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private Team team;
}

📌 Q도메인 클래스가 생성되기 위해서는 엔티티 클래스에 @Entity가 붙어 있어야 함

 

 

3) QMember

// EntityManager를 통해 JPAQueryFactory 생성
JPAQueryFactory queryFactory = new JPAQueryFactory(em);

QMember m = QMember.member;

Member result = queryFactory
    .selectFrom(m)
    .where(m.username.eq("현지"))
    .fetchOne();

 

  • selectFrom
    • select + from
  • where
    • 조건
  • eq()
    • equals
  • fetchOne()
    • 단일 결과
  • fetch()
    • 리스트 결과
  • fetchFirst()
    • limit 1
  • fetchCount()
    • count 쿼리

 

 

4) MemberRepositoryImpl

@Repository
public class MemberQueryRepository {

    private final JPAQueryFactory queryFactory;

    public MemberQueryRepository(EntityManager em) {
        this.queryFactory = new JPAQueryFactory(em);
    }

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

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

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


✅ Q클래스

QueryDSL이 @Entity 클래스 기반으로 자동 생성하는 메타 모델 클래스

 

@Entity
public class Member {
    @Id
    private Long id;
    private String username;
    private int age;
}

 

👉 생성시 자동으로 QMember.java 클래스가 만들어 진다.

public class QMember extends EntityPathBase<Member> {

    public static final QMember member = new QMember("member");

    public final StringPath username = createString("username");
    public final NumberPath<Integer> age = createNumber("age", Integer.class);
    public final NumberPath<Long> id = createNumber("id", Long.class);

}

 

👉 Q클래스 사용하기

 

QMember member = QMember.member;

Member result = queryFactory
    .selectFrom(member)               
    .where(member.username.eq("현지")) //eq를 사용
    .fetchOne();