[Java] QueryDSL(2) | QueryDSL 문법 응용 완벽 정리(예제 포함)

Photo of author
Written By 완두콩

3년차 웹 개발자.
만 28세.
화이팅!

지난 글 :

 




 

저번 포스팅에 이어서 이번 포스팅에서는 QueryDSL을 응용하여 사용하는 방법에 대해서 알아보겠습니다. SQL 쿼리와 마찬가지로 QueryDSL에서는 WHERE절, AND와 OR 연산, ORDER, GROUP 등 동일한 기능을 사용할 수 있다고 말씀드릴 수 있습니다. 아래 설명드리는 예제를 보고 따라하신다면 금방 익숙해지실 수 있을겁니다!

 

조건문

조건문 데이터베이스에서 원하는 데이터를 검색하는 데 매우 중요한 부분입니다. QueryDSL을 사용하여 간단하고 유연하게 조건을 처리하는 방법을 알아봅시다.

 

WHERE

데이터베이스에서 특정 조건을 만족하는 데이터를 검색할 때, WHERE 절을 사용합니다. QueryDSL에서 WHERE 절을 처리하는 방법은 간단합니다.

// QueryDSL을 사용하여 WHERE 절 처리하는 예제
List<User> users = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.gt(18)) // 나이가 18세보다 큰 사용자 검색
    .fetch();

위의 예제에서 qUser.age.gt(18) 부분은 사용자의 나이가 18세보다 큰지를 검사하는 조건을 의미합니다. gt는 “보다 큼”을 의미하는 비교 연산자입니다.

 

AND, OR

여러 개의 조건을 조합하여 데이터를 검색해야 할 때, AND나 OR 연산자를 사용합니다. QueryDSL에서는 다음과 같이 간단하게 조합할 수 있습니다.

// QueryDSL을 사용하여 AND, OR 연산자 처리하는 예제
List<User> users = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.gt(18).and(qUser.gender.eq("남성"))) // 나이가 18세보다 크고 성별이 "남성"인 사용자 검색
    .fetch();

위의 예제에서 qUser.age.gt(18).and(qUser.gender.eq(“남성”)) 부분은 나이가 18세보다 크고 성별이 “남성”인 사용자를 검색하는 조건을 의미합니다. and 메서드를 사용하여 두 개의 조건을 AND로 조합하였습니다.

 

조건부 값 처리

조건에 따라 다른 값으로 검색해야 할 때, QueryDSL에서는 Case를 사용하여 간단하게 처리할 수 있습니다.

// QueryDSL을 사용하여 조건부 값 처리하는 예제
String gender = "남성"; // 검색할 성별 조건
List<User> users = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.gt(18).and(qUser.gender.eq(
        new CaseBuilder()
            .when(gender.equals("남성")).then("M")
            .when(gender.equals("여성")).then("F")
            .otherwise("UNKNOWN")
    ))) // 나이가 18세보다 크고 입력받은 성별 조건에 따라 검색
    .fetch();

위의 예제에서 qUser.gender.eq(…) 부분은 조건부 값 처리를 의미합니다. 성별이 “남성”인 경우에는 “M”, “여성”인 경우에는 “F”, 그 외에는 “UNKNOWN”으로 검색합니다.

 

조건문에 대해 알아보았으니, 다음으로 정렬에 대해 알아보겠습니다.

 




 

정렬

데이터베이스에서 검색 결과를 특정 기준으로 정렬해야 할 때, 정렬 처리는 매우 중요한 요소입니다. QueryDSL을 사용하여 데이터를 원하는 순서대로 정렬하는 방법을 알아봅시다.

 

ORDER BY 절 사용하기

ORDER BY 절을 사용하여 결과를 오름차순 또는 내림차순으로 정렬할 수 있습니다. QueryDSL에서 ORDER BY 절을 처리하는 방법은 다음과 같습니다.

// QueryDSL을 사용하여 ORDER BY 절 처리하는 예제
List<User> users = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.gt(18))
    .orderBy(qUser.name.asc()) // 이름 오름차순 정렬
    .fetch();

위의 예제에서 qUser.name.asc() 부분은 이름을 오름차순으로 정렬하는 것을 의미합니다. asc()는 오름차순을 나타내는 메서드입니다. 내림차순으로 정렬하려면 desc() 메서드를 사용하면 됩니다.

 

다중 정렬

때로는 여러 개의 기준으로 데이터를 정렬해야 할 수 있습니다. QueryDSL에서는 다중 정렬 처리를 아주 간단하게 할 수 있습니다.

// QueryDSL을 사용하여 다중 정렬 처리하는 예제
List<User> users = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.gt(18))
    .orderBy(qUser.name.asc(), qUser.age.desc()) // 이름 오름차순, 나이 내림차순으로 정렬
    .fetch();

위의 예제에서 qUser.name.asc(), qUser.age.desc() 부분은 이름을 오름차순으로 정렬하고, 그 다음으로 나이를 내림차순으로 정렬하는 것을 의미합니다.

 

정렬 기준 변경

때로는 사용자의 입력에 따라 정렬 기준을 변경해야 할 수 있습니다. QueryDSL을 사용하면 이러한 상황에서도 유연하게 대응할 수 있습니다.

// QueryDSL을 사용하여 정렬 기준을 변경하는 예제
String orderByField = "age"; // 사용자 입력에 따라 정렬 기준을 받음
List<User> users = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.gt(18))
    .orderBy(orderByField.equals("age") ? qUser.age.asc() : qUser.name.asc()) // 사용자 입력에 따라 나이 또는 이름으로 정렬
    .fetch();

위의 예제에서 orderByField.equals(“age”) ? qUser.age.asc() : qUser.name.asc() 부분은 사용자가 나이를 선택한 경우 나이로, 그렇지 않은 경우 이름으로 정렬하는 것을 의미합니다. 이렇게 하면 사용자의 입력에 따라 동적으로 정렬 기준을 변경할 수 있습니다.

 

정렬 처리에 대해 알아보았으니, 다음으로 페이징에 대해 알아보겠습니다.

 




 

페이징

검색 결과가 많을 경우에는 페이징 처리를 통해 일부 데이터만 조회하는 것이 일반적입니다. QueryDSL을 사용하여 페이징 처리를 구현하는 방법을 알아봅시다.

 

LIMIT, OFFSET

페이징 처리를 위해 데이터베이스에서는 LIMIT과 OFFSET 절을 사용합니다. LIMIT은 가져올 데이터의 개수를, OFFSET은 시작 위치를 나타냅니다. QueryDSL에서는 다음과 같이 간단하게 사용할 수 있습니다.

// QueryDSL을 사용하여 LIMIT, OFFSET 절 처리하는 예제
int pageSize = 10; // 페이지당 보여줄 데이터 개수
int pageNo = 1; // 페이지 번호
List<User> users = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.gt(18))
    .orderBy(qUser.name.asc())
    .limit(pageSize)
    .offset((pageNo - 1) * pageSize) // 페이지 번호에 따른 OFFSET 계산
    .fetch();

위의 예제에서 limit(pageSize) 부분은 한 페이지당 보여줄 데이터 개수를 의미하며, offset((pageNo – 1) * pageSize) 부분은 페이지 번호에 따라 올바른 시작 위치를 계산합니다.

 

페이징에 대해 알아보았으니, 다음으로 집합에 대해 알아보겠습니다.

 




 

집합

데이터베이스에서 집합 함수를 사용하여 특정 필드의 합계, 평균, 최대값, 최소값 등을 계산할 수 있습니다. QueryDSL을 사용하여 집합 처리를 구현하는 방법을 알아봅시다.

 

집계 함수

집계 함수를 사용하여 데이터의 합계, 평균, 최대값, 최소값 등을 계산할 수 있습니다. QueryDSL에서는 다음과 같이 간단하게 집계 함수를 사용할 수 있습니다.

// QueryDSL을 사용하여 집계 함수 사용하는 예제
long totalAge = queryFactory
    .select(qUser.age.sum()) // 나이의 합계 계산
    .from(qUser)
    .where(qUser.age.gt(18))
    .fetchOne();

위의 예제에서 qUser.age.sum() 부분은 나이의 합계를 계산하는 집계 함수를 의미합니다. 이와 같이 간단하게 원하는 집계 함수를 사용할 수 있습니다.

 

그룹핑

때로는 데이터를 그룹으로 묶어서 집계 함수를 사용해야 할 때가 있습니다. 이럴 때는 GROUP BY 절을 사용하여 그룹핑 처리를 합니다. QueryDSL에서는 다음과 같이 그룹핑 처리를 할 수 있습니다.

// QueryDSL을 사용하여 그룹핑 처리하는 예제
List<Tuple> ageStats = queryFactory
    .select(qUser.gender, qUser.age.avg(), qUser.age.max(), qUser.age.min()) // 성별로 그룹핑하여 나이의 평균, 최대값, 최소값 계산
    .from(qUser)
    .where(qUser.age.gt(18))
    .groupBy(qUser.gender)
    .fetch();

위의 예제에서 groupBy(qUser.gender) 부분은 성별로 그룹핑을 하겠다는 것을 의미합니다. 이렇게 하면 성별별로 나이의 평균, 최대값, 최소값을 계산할 수 있습니다.

 

서브쿼리

때로는 서브쿼리를 사용하여 더 복잡한 집합 처리를 해야 할 수도 있습니다. QueryDSL에서는 다음과 같이 서브쿼리를 사용할 수 있습니다.

// QueryDSL을 사용하여 서브쿼리 사용하는 예제
List<User> youngUsers = queryFactory
    .selectFrom(qUser)
    .where(qUser.age.in(
        JPAExpressions
            .select(qUser.age)
            .from(qUser)
            .where(qUser.age.lt(30))
    )) // 나이가 30 미만인 사용자 검색
    .fetch();

위의 예제에서 JPAExpressions.select(qUser.age).from(qUser).where(qUser.age.lt(30)) 부분은 서브쿼리를 사용하여 나이가 30 미만인 사용자를 검색하는 것을 의미합니다. 이렇게 서브쿼리를 사용하여 더 복잡한 조건을 처리할 수 있습니다.

 

집합 처리에 대해 알아보았으니, 이제 예제를 통해 QueryDSL을 실제로 활용해보겠습니다.

 




 

예제) 게시물 검색 기능 구현하기

이번 예제에서는 게시물 검색 기능을 구현해보겠습니다. 게시물은 제목(title)과 내용(content) 필드를 가지고 있으며, 사용자가 입력한 키워드로 게시물을 검색하는 기능을 개발하려고 합니다.

 

도메인 엔티티 설정

먼저, 게시물을 나타내는 도메인 엔티티를 정의합니다.

@Entity
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    private String content;

    // Getters and setters...
}

 

검색 조건에 따른 QueryDSL 쿼리 작성

이제 사용자가 입력한 키워드에 따라 게시물을 검색하는 QueryDSL 쿼리를 작성합니다.

public List<Post> searchPosts(String keyword) {
    List<Post> posts = queryFactory
        .selectFrom(qPost)
        .where(qPost.title.contains(keyword).or(qPost.content.contains(keyword)))
        .fetch();
    return posts;
}

위의 예제에서 qPost.title.contains(keyword).or(qPost.content.contains(keyword)) 부분은 입력받은 키워드가 제목(title) 또는 내용(content)에 포함되어 있는 게시물을 검색하는 것을 의미합니다.

 

페이징 처리 추가하기

검색 결과가 많을 경우 한 번에 모든 결과를 보여주는 것은 사용자 경험에 좋지 않습니다. 따라서 페이징 처리를 추가하여 한 페이지에 일정 개수의 결과만 보여줄 수 있도록 합니다.

public List<Post> searchPosts(String keyword, int pageNo, int pageSize) {
    List<Post> posts = queryFactory
        .selectFrom(qPost)
        .where(qPost.title.contains(keyword).or(qPost.content.contains(keyword)))
        .orderBy(qPost.createdDate.desc()) // 생성일자 기준 내림차순 정렬
        .offset((pageNo - 1) * pageSize) // 페이징 처리를 위한 OFFSET 설정
        .limit(pageSize) // 한 페이지에 보여줄 개수 설정
        .fetch();
    return posts;
}

위의 예제에서 orderBy(qPost.createdDate.desc()) 부분은 게시물을 생성일자 기준으로 내림차순으로 정렬하는 것을 의미합니다. 또한 offset((pageNo – 1) * pageSize)와 limit(pageSize) 부분은 페이징 처리를 위한 OFFSET과 개수를 설정하는 것을 의미합니다.

 

이와 같이 QueryDSL을 사용하면 Java 기반으로 유연한 쿼리를 작성할 수 있습니다. QueryDSL을 활용하면 복잡한 쿼리를 안전하고 편리하게 작성할 수 있으며, 데이터베이스와의 상호작용을 더욱 쉽고 유지보수하기 쉽게 만들어줍니다.

 



Leave a Comment