본문 바로가기
QueryDSL

QueryDsl에서 함수 사용

by 이상한나라의개발자 2024. 3. 27.

Expressions 클래스는 SQL 함수의 다양한 표현식을 Java 코드로 직접 사용할 수 있으며, JPA와 같이 Dialect(방언)에 등록된 내용만 호출할 수 있습니다. QueryDsl의 Expressions 클래스에서 자주 사용되는 다양한 함수의 사용 예시를 소개합니다. 

 

@Test
void Expressions_사용() {

    List<Tuple> result = queryFactory
            .select(
                    Expressions.currentDate().stringValue()
                            .concat(" ")
                            .concat(Expressions.currentTime().stringValue()).as("currentDate"),
                    // member.username.concat(member.age.stringValue())
                    Expressions.stringTemplate("concat({0},{1})",
                            member.username, member.age.stringValue())
                            .as("usernameAndAge"),
                    // member.username.upper()
                    Expressions.stringTemplate("upper({0})", member.username)
                            .as("usernameUpper"),
                    // member.age.abs()
                    Expressions.numberTemplate(Integer.class, "abs({0})", member.age)
                            .as("ageAbs"),
                    Expressions.stringTemplate("replace({0},{1},{2})", member.username, "member", "M").as("name"),

                    // null 처리
                    member.username.coalesce("Unknown").as("username"),
                    new CaseBuilder().when(member.age.gt(20))
                            .then("Adult")
                            .otherwise("Minor")
                            .as("adultStatus")

            )
            .from(member)
            .fetch();

    Expression<String> usernameAndAge = Expressions.stringPath("usernameAndAge");
    Expression<String> name = Expressions.stringPath("name");

    for (Tuple tuple : result) {
        String usernameAndAgeValue = tuple.get(usernameAndAge);
        String nameValue = tuple.get(name);
        System.out.println(usernameAndAgeValue);
        System.out.println("nameValue = " + nameValue);
    }
}

 

@Test
void Expressions_DTO() {

    Expression<String> stringConstantExpress = Expressions.as(Expressions.constant("우리나라"), "countryName");
    Expression<String> stringTemplateExpression = Expressions.stringTemplate("function('UPPER', {0})", member.username).as("usernameUpper2");

    List<MemberTestDto> result = queryFactory
            .select(

                    Projections.fields(MemberTestDto.class,
                            Expressions.currentDate().stringValue().concat(" ").concat(Expressions.currentTime().stringValue()).as("currentDateTime"),
                            // member.username.concat(member.age.stringValue())
                            Expressions.stringTemplate("concat({0},{1})",member.username, member.age.stringValue()).as("usernameAndAge"),
                            // member.username.upper()
                            Expressions.stringTemplate("upper({0})", member.username).as("usernameUpper"),
                            // member.age.abs()
                            member.username.coalesce("Unknown").as("username"), // null 처리
                            new CaseBuilder().when(member.age.gt(20))
                                    .then("Adult")
                                    .otherwise("Minor")
                                    .as("adultStatus"),
                            Expressions.stringTemplate("replace({0},{1},{2})", member.username, "member", "M").as("replaceName"),
                            // 상수 표현식, 상수는 직접적으로 as를 줄수 없기 때문에 Expressions로 한번 더 감싼다.
                            stringConstantExpress,
                            stringTemplateExpression,
                            Expressions.numberTemplate(Integer.class, "abs({0})", member.age).as("ageAbs"),
                            Expressions.numberTemplate(Integer.class, "{0}+{1}", 1, 2).as("plusNumber")
                    )
            )
            .from(member)
            .fetch();

    //Expression<String> usernameAndAge = Expressions.stringPath("usernameAndAge");

    for (MemberTestDto memberTestDto : result) {
        System.out.println("memberTestDto.getUsernameAndAge() = " + memberTestDto.getUsernameAndAge());
        System.out.println("memberTestDto.getCurrentDate() = " + memberTestDto.getCurrentDateTime());
        System.out.println("memberTestDto.getUsernameUpper() = " + memberTestDto.getUsernameUpper());
        System.out.println("memberTestDto.getUsername() = " + memberTestDto.getUsername());
        System.out.println("memberTestDto.getAdultStatus() = " + memberTestDto.getAdultStatus());
        System.out.println("memberTestDto.getCountryName() = " + memberTestDto.getCountryName());
        System.out.println("memberTestDto.getReplaceName() = " + memberTestDto.getReplaceName());
        System.out.println("memberTestDto.getCountryName() = " + memberTestDto.getCountryName());
        System.out.println("memberTestDto.getPlusNumber() = " + memberTestDto.getPlusNumber());
        System.out.println("memberTestDto.getUsernameUpper2() = " + memberTestDto.getUsernameUpper2());
        System.out.println("memberTestDto.getCurrentDate() = " + memberTestDto.getCurrentDate());
    }
}

 

위 예시에서 보면 함수를 사용하기 위해 Expressions, Expression 클래스를 사용하였습니다. 이제 해당 클래스에 대해서 알아보도록 하겠습니다. queryDsl에서 Expression, Expressions는 쿼리 구성의 핵심 요소입니다. 이들은 쿼리의 다양한 부분(select, where, group by, order by)을 표현하는데 사용됩니다. 

 

Expression

Expression은 QueryDsl에서 가증 기본이 되는 인터페이스로, 쿼리의 모든 구성 요소(필드, 상수, 함수 호출 등)를 추상화합니다. 이 인터페이스는 쿼리 내에서 사용될 수 있는 모든 표현식의 타입을 정의합니다. 예를 들어, 엔티티의 속성, 연산 결과, 문자열 연결 등을 Expression을 통해 처리할 수 있습니다.

 

주요 메서드

Expression 인터페이스는 자체는 직접적으로 사용자에 의해 구현되는 것이 아니라, QueryDsl에서 제공하는 다양한 구현체 BooleanExpression, StringExpression 등을 통해 사용됩니다.

 

Expressions

Expressions는 Expression 인터페이스의 구체적인 인터페이스를 생성하기 위한 편의 메서드를 제공하기 위한 유틸 클래스입니다.

다르게 설명드리면, QueryDsl에서 쿼리를 작성할 때 다양한 조건이나 값, 함수 등을 Expression으로 표현해야 합니다. 예를 들어, 특정 필드가 어떤 값과 같다는 조건, 또는 문자열을 합치는 연산 등이 모두 표현식입니다. Expression 인터페이스는 이러한 모든 종류의 표현식을 추상화한 것으로, 실제로 쿼리를 구성하는 데 사용되는 구체적인 객체들이 이 인퍼페이스를 구현하고 있습니다.

 

Expressions 클래스는 이러한 Expression 객체들을 쉽게 만들 수 있도록 도와주는 도구 입니다. 다시 말해, Expressions는 개발자가 직접 Expression 객체를 생성하는 복잡한 과정을 대신해, 필요한 표현식을 간단한 방법으로 만들어 내는 공장으로 생각하시면 됩니다.

이 클래스는 정적메서드들로 구성되어 잇어서, 인스턴스를 생성하지 않고도 바로 메서드를 호출하여 표현식을 만들 수 있습니다.

 

아래와 같이 두 숫자를 더하는 표현식이 필요하다고 한다면, Expressions 클래스의 numberTemplate 메서드를 사용할 수 있습니다.

NumberExpression<Integer> plusNumber = Expressions.numberTemplate(Integer.class, "{0}+{1}", 1, 2);

 

이 코드는 두 숫자 1,2 를 더하는 표현식을 구성합니다. "{0},{1}" 은 표현식의 템플릿이며, 1,2 는 표현식에 들어갈 값입니다. 최종적으로 plusNumber 연산은 Expression 객체가 됩니다.

 

결론적으로 Expressions는 주로 특정 함수를 호출하거나, 복잡합 표현식을 만들기 위해 사용되는 유틸 클래스입니다.

 

상수 표현식 생성 ( constant )

상수 표현식은 as를 직접 생성할 수 없기에, Expressions로 한번 더 감싸야 합니다.

Expression<String> constantExpression = Expressions.constant("우리나라");
Expression<String> stringConstantExpress = Expressions.as(Expressions.constant("우리나라"), "countryName");

 

문자열 템플릿 ( stringTemplate )

사용자 정의 문자열 표현식을 생성합니다. SQL 함수 호출이나 복잡한 문자열 연산 함수에 사용할 수 있습니다.

Expression<String> stringTemplateExpression = Expressions.stringTemplate("function('UPPER', {0})", member.username).as("usernameUpper2");
Expressions.stringTemplate("upper({0})", member.username).as("usernameUpper"),

 

숫자 템플릿 ( numberTemplate )

사용자 정의 숫자 표현식을 생성합니다. 계산이나 숫자 데이터를 처리하는 함수를 호출할 때 사용됩니다.

Expression<Integer> numberTemplateExpression = Expressions.numberTemplate(Integer.class, "ABS({0})", member.age);
Expressions.numberTemplate(Integer.class, "{0}+{1}", 1, 2).as("plusNumber")

 

불리언 템플릿 ( booleanTemplate )

복잡한 논리 조건을 만들 때 사용됩니다.

BooleanExpression booleanTemplateExpression = Expressions.booleanTemplate("{0} > 18", member.age);

 

별칭 상용 ( as )

표현식에 별칭을 부여할 때 사용됩니다.

Expressions.stringTemplate("upper({0})", member.username).as("usernameUpper")

 

날짜/기간 표현식 ( dateTemplate, dateTimeTemplate, timeTemplate )

Expressions.currentDate().stringValue().concat(" ").concat(Expressions.currentTime().stringValue()).as("currentDateTime"),

'QueryDSL' 카테고리의 다른 글

QueryDsl 페이징 처리  (0) 2024.04.01
QueryDsl 동적쿼리 작성 방법  (1) 2024.03.26
QueryDsl 프로젝션 결과 반환  (0) 2024.03.25
QueryDsl 서브쿼리  (0) 2024.03.20
QueryDsl 조인  (0) 2024.03.20