Q클래스와 Path 클래스, JPAQueryFactory

haruceki·2025년 2월 12일

Q클래스

Q클래스는 QueryDSL 사용하시 자동 생성되는 엔티티 기반 클래스이다. JPA 엔티티를 기반으로 동적으로 쿼리를 작성할 수 있도록 지원한다. JPAQueryFactory와 함께 사용하면 가독성이 좋고 직관적인 쿼리 작성이 가능하다.

gradle의 경우 Q클래스의 자동 생성 위치는 build/generated/sources/annotationProcessor/java/main/패키지 경로~ 이다.

Path 클래스의 메서드

Q클래스 내부에서 엔티티의 필드들을 표현하는 경로 객체이다.
즉, Q클래스는 엔티티 전체를 대표하는 클래스라면, Path 클래스는 그 안의 필드들을 타입에 맞게 다루는 객체이다.
-> Q클래스 내부에 Path 클래스가 포함되어 있다.

QueryDSL 타입설명사용 예시
StringPath문자열 필드QUser.user.name.eq("John")
NumberPath<T>숫자 필드 (Integer, Long, Double, etc.)QUser.user.age.gt(18)
BooleanPathBoolean 타입 필드QUser.user.active.isTrue()
DatePath<T>java.sql.Date 타입 날짜 필드QOrder.order.date.eq(LocalDate.of(2024, 2, 13))
DateTimePath<T>java.time.LocalDateTime 등의 필드QOrder.order.createdAt.before(LocalDateTime.now())
TimePath<T>java.sql.Time 타입 필드QEvent.event.startTime.after(LocalTime.of(14, 0))
SimplePath<T>임의의 객체 타입 필드QUser.user.customField.eq(customObject)
EnumPath<T>Enum 타입 필드QUser.user.role.eq(Role.ADMIN)
ComparablePath<T>Comparable<T> 인터페이스를 구현한 객체QUser.user.someComparableField.between(a, b)
BeanPath<T>Java Bean 객체 타입QUser.user.address.city.eq("Seoul")
ArrayPath<T, A>배열 필드 (T[] 타입)QUser.user.tags.contains("Spring")
CollectionPath<E, Q>Collection<E> 타입 필드QUser.user.roles.contains(Role.ADMIN)
SetPath<E, Q>Set<E> 타입 필드QUser.user.permissions.contains(Permission.READ)
ListPath<E, Q>List<E> 타입 필드QUser.user.friends.contains(QUser.user)
MapPath<K, V, Q>Map<K, V> 타입 필드QUser.user.attributes.get("nickname").eq("Johnny")

✔ 1️⃣ 기본 필드 메서드

QUser user = QUser.user;

user.id          // NumberPath<Long> -> 숫자 필드
user.username    // StringPath      -> 문자열 필드
user.age         // NumberPath<Integer> -> 숫자 필드
user.isDelete    // BooleanPath     -> 불린 필드

✔ 2️⃣ 조건 메서드

user.username.eq("John")       // username = 'John'
user.age.goe(20)               // age >= 20
user.age.loe(30)               // age <= 30
user.username.contains("oh")   // username LIKE '%oh%'
user.isDelete.isFalse()        // isDelete = false
user.id.in(1L, 2L, 3L)         // id IN (1,2,3)

✔ 3️⃣ BooleanExpression 활용
-> 파라미터가 null이면 조건을 제외할 수 있다 (JPQL에서는 불가능)

public BooleanExpression usernameEq(String username) {
    return username != null ? QUser.user.username.eq(username) : null;
}

public BooleanExpression ageGoe(Integer age) {
    return age != null ? QUser.user.age.goe(age) : null;
}

public List<User> findUsers(String username, Integer age) {
    return queryFactory
        .selectFrom(QUser.user)
        .where(usernameEq(username), ageGoe(age)) // null이면 무시됨
        .fetch();
}

JPAQueryFactory

QueryDSL에서 JPA 기반 쿼리를 쉽게 생성할 수 있도록 도와주는 객체이다. JPQL이나 Criteria API보다 가독성이 좋고 타입 안정성이 보장된다.

EntityManager 기반으로 동작하며, (Q클래스 기반의) 타입 안정성을 보장, 동적 쿼리 작성 가능(BooleanExpression 활용), 다양한 메서드를 지원한다.

JPAQueryFactory 사용한 쿼리 작성 예시

  • 기본 조회 (단일 결과 조회)
@Autowired
private JPAQueryFactory queryFactory;

public User findByUsername(String username) {
    return queryFactory
        .selectFrom(QUser.user)
        .where(QUser.user.username.eq(username))
        .fetchOne();
}
  • 다중 조건 조회
public List<User> findUsers(String username, Integer minAge) {
    return queryFactory
        .selectFrom(QUser.user)
        .where(
            QUser.user.username.like("%" + username + "%"), 
            QUser.user.age.goe(minAge) // age >= minAge
        )
        .fetch();
}
  • 정렬 및 페이징
public List<User> findUsersWithPagination(int page, int size) {
    return queryFactory
        .selectFrom(QUser.user)
        .orderBy(QUser.user.age.desc())
        .offset((page - 1) * size)
        .limit(size)
        .fetch();
}
메서드설명
fetchOne()단일 결과 조회 (여러 개면 예외)
fetchFirst()첫 번째 결과만 조회
fetch()리스트 조회 (비어 있으면 빈 리스트)
fetchResults()페이징용 (fetchCount() + fetch()) (⚠️ Hibernate 6부터 지원 중단)
fetchCount()개수 조회 (⚠️ Hibernate 6부터 지원 중단)
count(), sum(), avg(), max(), min()집계 함수
groupBy(), having()그룹화 및 조건
orderBy()정렬
limit(), offset()페이징
profile
희망도 절망도 없이 매일 코딩을 한다.

0개의 댓글