1. QueryDSL이란?
QueryDSL은 타입 안전한 SQL 및 JPQL을 생성할 수 있도록 도와주는 프레임워크입니다. JPQL보다 가독성이 뛰어나고, 컴파일 타임에 오류를 잡을 수 있어 동적 쿼리를 작성할 때 매우 유용합니다.
2. QueryDSL 설정 및 기본 사용법
(1) QueryDSL 설정 (Gradle 기준)
dependencies {
implementation 'com.querydsl:querydsl-jpa:5.0.0'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api:3.1.0'
}
QueryDSL을 사용하면 Q타입 클래스를 자동 생성해야 하므로, IDE에서 빌드 후 생성된 Q타입 클래스를 확인해야 합니다.
(2) QueryDSL 기본 사용법
1) Q클래스 생성
import com.querydsl.core.types.dsl.EntityPathBase;
QMember member = QMember.member;
2) 단순 조회 쿼리 작성
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
Member result = queryFactory
.selectFrom(member)
.where(member.username.eq("testUser"))
.fetchOne();
3) 여러 조건을 사용하는 WHERE 절
List<Member> results = queryFactory
.selectFrom(member)
.where(
member.age.gt(18),
member.status.eq(Status.ACTIVE)
)
.fetch();
QueryDSL의 where() 메서드는 여러 조건을 가변 인자로 받을 수 있어 코드가 깔끔해집니다.
3. QueryDSL의 동적 쿼리 작성법
(1) BooleanBuilder 활용
BooleanBuilder를 사용하면 조건을 동적으로 추가할 수 있습니다.
BooleanBuilder builder = new BooleanBuilder();
if (username != null) {
builder.and(member.username.eq(username));
}
if (age != null) {
builder.and(member.age.gt(age));
}
List<Member> results = queryFactory
.selectFrom(member)
.where(builder)
.fetch();
(2) Where절 메서드 추출
코드를 더 깔끔하게 만들기 위해 where 절을 별도의 메서드로 추출할 수도 있습니다.
private BooleanExpression usernameEq(String username) {
return username != null ? member.username.eq(username) : null;
}
private BooleanExpression ageGt(Integer age) {
return age != null ? member.age.gt(age) : null;
}
List<Member> results = queryFactory
.selectFrom(member)
.where(usernameEq(username), ageGt(age))
.fetch();
이렇게 하면 가독성이 높아지고, 재사용성이 증가합니다.
4. QueryDSL을 활용한 복잡한 쿼리 작성
(1) 조인 (Join) 활용
List<Member> results = queryFactory
.selectFrom(member)
.join(member.team, QTeam.team)
.where(QTeam.team.name.eq("TeamA"))
.fetch();
(2) 서브쿼리 사용
QMember subMember = new QMember("subMember");
List<Member> results = queryFactory
.selectFrom(member)
.where(member.age.eq(
JPAExpressions
.select(subMember.age.max())
.from(subMember)
))
.fetch();
QueryDSL을 활용하면 JPQL에서는 어렵던 서브쿼리도 직관적으로 작성할 수 있습니다.
5. QueryDSL을 이용한 INSERT, UPDATE, DELETE
(1) QueryDSL을 이용한 INSERT
QueryDSL 자체로 INSERT 쿼리를 직접 지원하지 않음. JPA에서는 persist()를 사용하여 INSERT를 수행해야 함.
Member member = new Member();
member.setUsername("newUser");
member.setAge(25);
entityManager.persist(member);
(2) QueryDSL을 이용한 UPDATE
QueryDSL의 update()를 활용하여 벌크 업데이트 수행 가능.
long updatedRows = queryFactory
.update(member)
.set(member.age, member.age.add(1))
.where(member.status.eq(Status.ACTIVE))
.execute();
✅ 주의: 벌크 연산 후 entityManager.clear();로 영속성 컨텍스트 초기화 필요.
(3) QueryDSL을 이용한 DELETE
QueryDSL의 delete()를 활용하여 특정 조건의 데이터를 삭제 가능.
long deletedRows = queryFactory
.delete(member)
.where(member.age.lt(18))
.execute();
✅ 주의: DELETE도 벌크 연산이므로 entityManager.flush(); 및 entityManager.clear(); 필요.
QueryDSL 지원여부 | 방법 | |
INSERT | ❌ (지원 안 함) | entityManager.persist() 사용 |
UPDATE | ✅ (지원) | update().set().where().execute() |
DELETE | ✅ (지원) | delete().where().execute() |
6. QueryDSL 사용 시 장점과 주의점
(1) QueryDSL의 장점
✅ 타입 안전성: 런타임이 아닌 컴파일 타임에 오류를 잡을 수 있음
✅ 가독성: JPQL보다 직관적인 문법
✅ 동적 쿼리 최적화: BooleanBuilder, Where절 분리 등을 활용하여 유연한 쿼리 작성 가능
✅ 복잡한 SQL 처리 가능: 서브쿼리, GroupBy, Join 등 다양한 SQL 기능 지원
(2) QueryDSL 사용 시 주의점
⚠ 빌드 시 Q타입 클래스 생성 필요: Q클래스가 자동 생성되지 않으면 빌드를 수동 실행해야 함
7. 결론
QueryDSL은 타입 안전한 동적 쿼리 작성을 지원하며, 복잡한 SQL을 쉽게 작성할 수 있는 강력한 도구입니다.
특히 JPQL보다 가독성이 좋고 유지보수성이 뛰어나기 때문에, Spring Boot + JPA 환경에서는 매우 편리하다.
'Back-End > JPA' 카테고리의 다른 글
JPA 트랜잭션 관리와 @Transactional의 동작 방식 (0) | 2025.03.09 |
---|---|
JPA랑 Hibernate (0) | 2025.03.09 |
JPA에서 일어나는 영속성 컨텍스트의 동작 원리 (1차 캐시, 변경 감지 등) (0) | 2025.03.09 |
Spring Data JPA에서 Native Query 사용법과 주의점 (0) | 2025.03.09 |
JPA 성능 최적화: N+1 문제 해결 및 페치 전략 (Fetch Join, Entity Graph) (0) | 2025.03.09 |