Skip to content

확장 인터페이스

querydsl-ktx는 8개의 확장 인터페이스를 제공하며, 각각 특정 QueryDSL 표현식 타입에 스코프가 지정됩니다. 모든 함수는 null-safe합니다: null 인자는 조건을 건너뛰게 합니다.


개요

인터페이스표현식 타입주요 함수
BooleanExpressionExtensionsBooleanExpressionand, or, andAnyOf, orAllOf, eq, nullif, coalesce
SimpleExpressionExtensionsSimpleExpression<T>eq, ne, in, notIn
ComparableExpressionExtensionsComparableExpression<T>gt, goe, lt, loe, between, nullif, coalesce, rangeTo
NumberExpressionExtensionsNumberExpression<T>gt, goe, lt, loe, between, nullif, coalesce, rangeTo
StringExpressionExtensionsStringExpressioncontains, startsWith, endsWith, like, matches, nullif, coalesce
TemporalExpressionExtensionsTemporalExpression<T>after, before
CollectionExpressionExtensionsCollectionExpressionBase<T, E>contains
SubQueryExtensionsEntityPath<T>exists, notExists

BooleanExpressionExtensions

Null-safe AND/OR 결합자. 동적 WHERE 절 구성의 기반입니다.

함수

함수시그니처SQL
andBooleanExpression?.and(BooleanExpression?)a AND b
orBooleanExpression?.or(BooleanExpression?)a OR b
andAnyOfBooleanExpression?.andAnyOf(List<BooleanExpression?>)a AND (b OR c OR ...)
orAllOfBooleanExpression?.orAllOf(List<BooleanExpression?>)a OR (b AND c AND ...)
eqBooleanExpression?.eq(Boolean?)active = true
nullifBooleanExpression?.nullif(Boolean?)NULLIF(active, true)
coalesceBooleanExpression?.coalesce(Boolean?)COALESCE(active, false)

예제

kotlin
// AND: null 쪽은 무시됨
val predicate = (entity.active eq true) and (entity.name eq name)

// OR 그룹
val rolePredicate = (entity.role eq "ADMIN") or (entity.role eq "MANAGER")

// AND와 OR 서브그룹
val complex = (entity.active eq true) andAnyOf listOf(
    entity.role eq role,
    entity.department eq department,
)
sql
-- AND (name = 'John')
active = true AND name = 'John'

-- AND (name = null) -> 왼쪽만 남음
active = true

-- OR 그룹
role = 'ADMIN' OR role = 'MANAGER'

-- AND와 OR 서브그룹
active = true AND (role = ? OR department = ?)

vararg 오버로드 v1.2.0+

andAnyOforAllOf는 vararg BooleanExpression?도 받습니다. Kotlin은 infix 함수에 vararg를 허용하지 않으므로 점 표기법으로 호출합니다: predicate.andAnyOf(p1, p2, p3).


SimpleExpressionExtensions

모든 표현식 타입에 대한 동등성 및 멤버십 연산자.

함수

함수시그니처SQL
eqSimpleExpression<T>?.eq(T?)status = ?
eqSimpleExpression<T>?.eq(Expression<in T>?)status = default_status
eqSimpleExpression<T>?.eq(SubQueryExpression<T>?)price = (SELECT ...)
neSimpleExpression<T>?.ne(T?)status != ?
neSimpleExpression<T>?.ne(Expression<in T>?)status != default_status
inSimpleExpression<T>?.in(Collection<T>?)status IN (?, ?)
inSimpleExpression<T>?.in(SubQueryExpression<T>?)id IN (SELECT ...)
notInSimpleExpression<T>?.notIn(Collection<T>?)status NOT IN (?, ?)
notInSimpleExpression<T>?.notIn(SubQueryExpression<T>?)id NOT IN (SELECT ...)
inChunkedSimpleExpression<T>?.inChunked(Collection<T>?)col IN (?) OR col IN (?)
eqAllSimpleExpression<T>?.eqAll(CollectionExpression<*, in T>?) / eqAll(SubQueryExpression<T>?)col = ALL (...)
eqAnySimpleExpression<T>?.eqAny(CollectionExpression<*, in T>?) / eqAny(SubQueryExpression<T>?)col = ANY (...)
neAllSimpleExpression<T>?.neAll(CollectionExpression<*, in T>?)col <> ALL (...)
neAnySimpleExpression<T>?.neAny(CollectionExpression<*, in T>?)col <> ANY (...)

예제

kotlin
// 동등성
entity.status eq "ACTIVE"              // status = 'ACTIVE'
entity.status eq null                  // null (건너뛰기)

// 부등식
entity.status ne "DELETED"             // status != 'DELETED'

// IN / NOT IN
entity.status `in` listOf("A", "B")   // status IN ('A', 'B')
entity.status notIn listOf("C")       // status NOT IN ('C')
entity.status `in` null               // null (건너뛰기)

// 대량 IN절 자동 분할 (기본 1000개씩)
entity.id inChunked largeIdList   // id IN (1..1000) OR id IN (1001..2000)
entity.id.inChunked(ids, 500)     // 커스텀 청크 사이즈
sql
status = 'ACTIVE'
status != 'DELETED'
status IN ('A', 'B')
status NOT IN ('C')

Oracle의 IN 절 제한 대응

Oracle은 단일 IN 절에 1000개 항목 제한이 있습니다. inChunked는 대량 컬렉션을 자동으로 여러 IN 절로 분할하고 OR로 연결합니다. 기본 청크 사이즈는 1000이며, 커스텀 사이즈 지정이 가능합니다.

서브쿼리 비교 v1.2.0+

eq, in, notInSubQueryExpression<T>? 오버로드도 제공합니다. JPAExpressions로 만든 서브쿼리와 null-safe하게 비교할 수 있습니다.

kotlin
import com.querydsl.jpa.JPAExpressions

// price가 모든 상품 중 최대 가격과 같은 상품
val maxPriceSubQuery = JPAExpressions.select(product.price.max()).from(product)
selectFrom(product).where(product.price eq maxPriceSubQuery).fetch()

// category가 서브쿼리 결과에 포함된 상품
val cheapCategoriesSubQuery = JPAExpressions
    .selectDistinct(product.category)
    .from(product)
    .where(product.price.lt(10000))
selectFrom(product).where(product.category `in` cheapCategoriesSubQuery).fetch()

서브쿼리 인자가 null이면 조건 자체가 건너뛰어지므로 (null 반환), 선택적 서브쿼리 필터를 if 분기 없이 그대로 끼울 수 있습니다.


ComparableExpressionExtensions

Comparable 타입(날짜, 문자열, enum 등)에 대한 비교 및 범위 연산자.

함수

함수시그니처SQL
gtComparableExpression<T>?.gt(T?)col > ?
goeComparableExpression<T>?.goe(T?)col >= ?
ltComparableExpression<T>?.lt(T?)col < ?
loeComparableExpression<T>?.loe(T?)col <= ?
betweenComparableExpression<T>?.between(Pair<T?, T?>)col BETWEEN ? AND ?
betweenComparableExpression<T>?.between(ClosedRange<T>)col BETWEEN ? AND ?
notBetweenComparableExpression<T>?.notBetween(Pair<T?, T?>)col NOT BETWEEN ? AND ?
between (역방향)T?.between(Pair<ComparableExpression<T>?, ComparableExpression<T>?>)lower <= ? AND upper >= ?
nullifComparableExpression<T>?.nullif(T?)NULLIF(col, ?)
coalesceComparableExpression<T>?.coalesce(T?)COALESCE(col, ?)
rangeToComparableExpression<T>..ComparableExpression<T>(between용 Pair 생성)
gtAllComparableExpression<T>?.gtAll(CollectionExpression<*, in T>?) / gtAll(SubQueryExpression<T>?)col > ALL (...)
gtAnyComparableExpression<T>?.gtAny(CollectionExpression<*, in T>?) / gtAny(SubQueryExpression<T>?)col > ANY (...)
goeAllComparableExpression<T>?.goeAll(CollectionExpression<*, in T>?) / goeAll(SubQueryExpression<T>?)col >= ALL (...)
goeAnyComparableExpression<T>?.goeAny(CollectionExpression<*, in T>?) / goeAny(SubQueryExpression<T>?)col >= ANY (...)
ltAllComparableExpression<T>?.ltAll(CollectionExpression<*, in T>?) / ltAll(SubQueryExpression<T>?)col < ALL (...)
ltAnyComparableExpression<T>?.ltAny(CollectionExpression<*, in T>?) / ltAny(SubQueryExpression<T>?)col < ANY (...)
loeAllComparableExpression<T>?.loeAll(CollectionExpression<*, in T>?) / loeAll(SubQueryExpression<T>?)col <= ALL (...)
loeAnyComparableExpression<T>?.loeAny(CollectionExpression<*, in T>?) / loeAny(SubQueryExpression<T>?)col <= ANY (...)

모든 비교 함수에는 다른 컬럼과 비교하기 위한 Expression<T> 오버로드도 있습니다.

예제

kotlin
// 단순 비교
entity.date gt startDate       // date > ?
entity.date goe startDate      // date >= ?
entity.date lt endDate         // date < ?
entity.date loe endDate        // date <= ?

// Pair를 사용한 BETWEEN (부분 범위 지원)
entity.date between (from to to)       // BETWEEN ? AND ?
entity.date between (from to null)     // date >= ?
entity.date between (null to to)       // date <= ?
entity.date between (null to null)     // null (건너뛰기)

// ClosedRange를 사용한 BETWEEN
entity.age between (20..60)            // BETWEEN 20 AND 60

// 역방향 BETWEEN: 값이 왼쪽, 표현식 경계가 오른쪽
now between (sale.startAt to sale.endAt)
// -> start_at <= now AND end_at >= now

// rangeTo 연산자 (..): Pair 생성 syntactic sugar
entity.date between (entity.startDate..entity.endDate)
// 동일: entity.date between (entity.startDate to entity.endDate)
sql
-- 전체 범위
created_at BETWEEN '2024-01-01' AND '2024-12-31'

-- 단측 (from만)
created_at >= '2024-01-01'

-- 단측 (to만)
created_at <= '2024-12-31'

-- ClosedRange
age BETWEEN 20 AND 60

선택적 날짜 범위를 위한 Pair 기반 between

Pair 오버로드는 날짜 범위 필터에서 가장 강력한 기능입니다. 하나의 표현식으로 네 가지 조합(양쪽 값, from만, to만, 둘 다 없음)을 모두 처리합니다. 그렇지 않으면 4분기 if/else가 필요합니다.

역방향 Between: 실전 활용 사례

역방향 between값이 왼쪽, 컬럼 경계가 오른쪽에 옵니다. 실무에서 의외로 자주 필요한 패턴입니다:

할인 기간 검증: 쿠폰이 지금 유효한지 확인:

kotlin
val now = LocalDateTime.now()
selectFrom(coupon)
    .where(now between (coupon.validFrom to coupon.validUntil))
    .fetch()
// SQL: valid_from <= '2025-04-10T12:00' AND valid_until >= '2025-04-10T12:00'

주문 금액이 할인 구간에 해당하는지 확인:

kotlin
val orderAmount = 50000
selectFrom(discountTier)
    .where(orderAmount between (discountTier.minAmount to discountTier.maxAmount))
    .fetch()
// SQL: min_amount <= 50000 AND max_amount >= 50000

이벤트 기간 내 주문 조회:

kotlin
// 주문일이 이벤트 기간 안에 있는지
selectFrom(order)
    .where(order.orderedAt between (event.startAt to event.endAt))
    .fetch()

역방향 between도 null-safe입니다: 값이 null이면 전체 표현식이 null을 반환합니다(건너뜀).

인프런에서 배운 BooleanExpression 패턴과의 비교

김영한 강의에서 배운 BooleanExpression 반환 메서드 패턴을 기억하시나요?

kotlin
// 강의 스타일: 필드마다 메서드 하나씩
fun isWithinPeriod(now: LocalDateTime?): BooleanExpression? {
    if (now == null) return null
    return event.startAt.loe(now).and(event.endAt.goe(now))
}

querydsl-ktx에서는 이 패턴이 별도 메서드 없이 인라인으로 표현됩니다:

kotlin
// querydsl-ktx: 메서드 추출 불필요
now between (event.startAt to event.endAt)

NumberExpressionExtensions

ComparableExpressionExtensions와 동일한 API이지만 NumberExpression용입니다.

산술 연산자 v1.2.0+

add, subtract, multiply, divide, mod도 동일한 null-safety 계약으로 사용할 수 있습니다. 어느 한쪽이 null이면 null을 반환하여 산술 표현식 자체가 건너뜁니다. 각 연산자는 값 오버로드와 Expression<T> 오버로드를 모두 제공합니다.

kotlin
entity.price add 1000             // price + 1000
entity.price multiply taxRate     // price * tax_rate (다른 컬럼)
entity.total divide quantity      // total / quantity

Kotlin 연산자 오버로드 v1.2.0+

Kotlin 산술 연산자 (+, -, *, /, %, 단항 -)도 표현식 빌딩 (정렬, 프로젝션, computed column) 용도로 제공됩니다. 위의 null-skip infix 형태와 달리 non-null 계약입니다 (양쪽 모두 필수).

kotlin
entity.price + 1000               // price + 1000
entity.price * taxRate            // price * tax_rate (다른 컬럼)
-entity.price                     // -price
(entity.price + entity.tax) * 2   // 괄호로 결합 가능

양쪽이 모두 보장될 때 + / - / * / / / %를 사용하고, 어느 한쪽이 null일 수 있어 표현식 자체를 건너뛰고 싶다면 add / subtract 등을 사용하세요.

별도 인터페이스인 이유

QueryDSL의 타입 계층에서 NumberExpressionComparableExpression확장하지 않습니다. 이는 QueryDSL의 설계 결정입니다. NumberExpressionComparableExpressionBase를 상속하고, ComparableExpression은 별도의 분기입니다. 따라서 querydsl-ktx는 NumberExpression에 특화된 병렬 연산자 세트를 제공합니다.

함수

함수시그니처SQL
gtNumberExpression<T>?.gt(T?)col > ?
goeNumberExpression<T>?.goe(T?)col >= ?
ltNumberExpression<T>?.lt(T?)col < ?
loeNumberExpression<T>?.loe(T?)col <= ?
betweenNumberExpression<T>?.between(Pair<T?, T?>)col BETWEEN ? AND ?
betweenNumberExpression<T>?.between(ClosedRange<T>)col BETWEEN ? AND ?
notBetweenNumberExpression<T>?.notBetween(Pair<T?, T?>)col NOT BETWEEN ? AND ?
between (역방향)T?.between(Pair<NumberExpression<T>?, NumberExpression<T>?>)lower <= ? AND upper >= ?
nullifNumberExpression<T>?.nullif(T?)NULLIF(col, ?)
coalesceNumberExpression<T>?.coalesce(T?)COALESCE(col, ?)
rangeToNumberExpression<T>..NumberExpression<T>(between용 Pair 생성)
addNumberExpression<T>?.add(T?) / add(Expression<T>?)col + ?
subtractNumberExpression<T>?.subtract(T?) / subtract(Expression<T>?)col - ?
multiplyNumberExpression<T>?.multiply(T?) / multiply(Expression<T>?)col * ?
divideNumberExpression<T>?.divide(T?) / divide(Expression<T>?)col / ?
modNumberExpression<T>?.mod(T?) / mod(Expression<T>?)col % ?
+ / - / * / / / %operator NumberExpression<T>.plus/minus/times/div/rem(T | Expression<T>)col + ? (non-null 계약)
단항 -operator NumberExpression<T>.unaryMinus()-col
gtAllNumberExpression<T>?.gtAll(CollectionExpression<*, in T>?) / gtAll(SubQueryExpression<T>?)col > ALL (...)
gtAnyNumberExpression<T>?.gtAny(CollectionExpression<*, in T>?) / gtAny(SubQueryExpression<T>?)col > ANY (...)
goeAllNumberExpression<T>?.goeAll(CollectionExpression<*, in T>?)col >= ALL (...)
goeAnyNumberExpression<T>?.goeAny(CollectionExpression<*, in T>?)col >= ANY (...)
ltAllNumberExpression<T>?.ltAll(CollectionExpression<*, in T>?)col < ALL (...)
ltAnyNumberExpression<T>?.ltAny(CollectionExpression<*, in T>?)col < ANY (...)
loeAllNumberExpression<T>?.loeAll(CollectionExpression<*, in T>?)col <= ALL (...)
loeAnyNumberExpression<T>?.loeAny(CollectionExpression<*, in T>?)col <= ANY (...)

예제

kotlin
entity.price gt 10000
entity.price between (minPrice to maxPrice)
entity.score between (0..100)
entity.quantity loe maxQuantity

// 역방향 BETWEEN: 값이 왼쪽, 표현식 경계가 오른쪽
orderAmount between (tier.minAmount to tier.maxAmount)
// -> min_amount <= orderAmount AND max_amount >= orderAmount

// rangeTo 연산자 (..): Pair 생성 syntactic sugar
orderAmount between (tier.minAmount..tier.maxAmount)
sql
price > 10000
price BETWEEN ? AND ?
score BETWEEN 0 AND 100
quantity <= ?

ALL/Any 비교 v1.2.0+

QueryDSL은 컬렉션 또는 서브쿼리와 비교하기 위한 <op>All / <op>Any 멤버를 제공합니다. querydsl-ktx는 동일한 null-skip 시맨틱으로 래핑합니다.

kotlin
import com.querydsl.jpa.JPAExpressions

// Stationery 카테고리의 모든 price보다 큰 상품
val stationeryPrices = JPAExpressions.select(product.price).from(product)
    .where(product.category.eq("Stationery"))
selectFrom(product).where(product.price gtAll stationeryPrices).fetch()

// cheap 카테고리의 어떤 price와도 일치하는 상품
val cheapPrices = JPAExpressions.select(product.price).from(product)
    .where(product.price.lt(10000))
selectFrom(product).where(product.price eqAny cheapPrices).fetch()

QueryDSL 5.1.0의 멤버 커버리지는 비대칭이며 래퍼는 이를 그대로 미러링합니다.

함수CollectionExpressionSubQueryExpression
eqAll / eqAny (Simple, Comparable, Number)
neAll / neAny (Simple)✗ (멤버 부재)
gtAll / gtAny (Comparable, Number)
goeAll / ltAll / loeAll*Any (Comparable)
goeAll / ltAll / loeAll*Any (Number)✗ (NumberExpression 멤버 부재)

Number 컬럼에 누락된 SubQuery 변형이 필요하면, 기반 타입이 Comparable<T>일 경우 ComparableExpression view로 캐스팅하여 사용합니다.


StringExpressionExtensions

패턴 매칭 및 문자열 비교 연산자.

함수

함수시그니처SQL
containsStringExpression?.contains(String?)LIKE '%val%'
containsIgnoreCaseStringExpression?.containsIgnoreCase(String?)LOWER(col) LIKE LOWER('%val%')
startsWithStringExpression?.startsWith(String?)LIKE 'val%'
startsWithIgnoreCaseStringExpression?.startsWithIgnoreCase(String?)LOWER(col) LIKE LOWER('val%')
endsWithStringExpression?.endsWith(String?)LIKE '%val'
endsWithIgnoreCaseStringExpression?.endsWithIgnoreCase(String?)LOWER(col) LIKE LOWER('%val')
equalsIgnoreCaseStringExpression?.equalsIgnoreCase(String?)LOWER(col) = LOWER(?)
notEqualsIgnoreCaseStringExpression?.notEqualsIgnoreCase(String?)LOWER(col) != LOWER(?)
likeStringExpression?.like(String?)LIKE ?
likeIgnoreCaseStringExpression?.likeIgnoreCase(String?)LOWER(col) LIKE LOWER(?)
notLikeStringExpression?.notLike(String?)NOT LIKE ?
matchesStringExpression?.matches(String?)REGEXP ?
containsStringExpression?.contains(Expression<String>?)LIKE '%' || other_col || '%'
startsWithStringExpression?.startsWith(Expression<String>?)LIKE other_col || '%'
endsWithStringExpression?.endsWith(Expression<String>?)LIKE '%' || other_col
nullifStringExpression?.nullif(Expression<String>?)NULLIF(col, other_col)
nullifStringExpression?.nullif(String?)NULLIF(col, ?)
coalesceStringExpression?.coalesce(Expression<String>?)COALESCE(col, other_col)
coalesceStringExpression?.coalesce(String?)COALESCE(col, ?)

contains, startsWith, endsWith에는 Expression<String> 오버로드도 있습니다.

예제

kotlin
// 부분 문자열 검색
entity.name contains keyword              // name LIKE '%keyword%'
entity.name containsIgnoreCase keyword     // 대소문자 무시

// 접두사 / 접미사
entity.name startsWith prefix             // name LIKE 'prefix%'
entity.name endsWith suffix               // name LIKE '%suffix'

// 패턴과 정규식
entity.name like "J%n"                    // name LIKE 'J%n'
entity.email matches "^[a-z]+@.*"         // name REGEXP '^[a-z]+@.*'

// 대소문자 무시 동등성
entity.email equalsIgnoreCase email       // LOWER(email) = LOWER(?)
sql
name LIKE '%keyword%'
LOWER(name) LIKE LOWER('%keyword%')
name LIKE 'prefix%'
name LIKE '%suffix'
name LIKE 'J%n'
email REGEXP '^[a-z]+@.*'
LOWER(email) = LOWER(?)

리터럴 % / _ 매칭을 위한 escape 문자 v1.2.0+

패턴에 와일드카드가 아닌 리터럴 %_가 들어갈 때는 like, notLike, likeIgnoreCase 다음에 escape '\'를 체이닝합니다. SQL의 LIKE pattern ESCAPE 'char' 구문을 그대로 미러링합니다.

kotlin
name like "10\\%off" escape '\\'              // LIKE '10\%off' ESCAPE '\'
name notLike "10\\%off" escape '\\'           // NOT LIKE '10\%off' ESCAPE '\'
name likeIgnoreCase "10\\%OFF" escape '\\'    // LIKE '10\%OFF' ESCAPE '\' (대소문자 무시)

escapeBooleanExpression?.escape(Char) 형태입니다. like 계열 결과가 아닌 표현식에 호출하면 ExpressionException을 던집니다.


TemporalExpressionExtensions

날짜/시간 표현식을 위한 시간 비교 연산자.

함수

함수시그니처SQL
afterTemporalExpression<T>?.after(T?)col > ?
afterTemporalExpression<T>?.after(Expression<T>?)col > other_col
beforeTemporalExpression<T>?.before(T?)col < ?
beforeTemporalExpression<T>?.before(Expression<T>?)col < other_col

예제

kotlin
entity.createdAt after startDate     // created_at > ?
entity.createdAt before endDate      // created_at < ?

// 컬럼 비교
entity.endDate after entity.startDate  // end_date > start_date

// Null-safe
entity.createdAt after null          // null (건너뛰기)
sql
created_at > '2024-01-01'
created_at < '2024-12-31'
end_date > start_date

after/before vs gt/goe/lt/loe

TemporalExpression(날짜, 타임스탬프)에는 after/before를 사용하고, ComparableExpression이나 NumberExpression에는 gt/goe/lt/loe를 사용하세요. 생성되는 SQL은 같지만 서로 다른 QueryDSL 타입에 정의되어 있습니다.


CollectionExpressionExtensions

매핑된 컬렉션 필드(예: @ElementCollection, @ManyToMany)에 대한 멤버십 검사.

함수

함수시그니처SQL
containsCollectionExpressionBase<T, E>?.contains(E?)? IN (col)
containsCollectionExpressionBase<T, E>?.contains(Expression<E>?)other_col IN (col)

예제

kotlin
entity.roles contains "ADMIN"       // 'ADMIN' IN (roles)
entity.tags contains tag            // ? IN (tags), null-safe
sql
'ADMIN' IN (roles)

SubQueryExtensions

EXISTS / NOT EXISTS 서브쿼리 단축 빌더.

함수

함수시그니처SQL
existsEntityPath<T>.exists(vararg Predicate?)EXISTS (SELECT 1 FROM ...)
notExistsEntityPath<T>.notExists(vararg Predicate?)NOT EXISTS (SELECT 1 FROM ...)

예시

kotlin
// Before: 장황한 서브쿼리
JPAExpressions.selectOne()
    .from(orderItem)
    .where(orderItem.orderId.eq(order.id))
    .exists()

// After: 간결
orderItem.exists(orderItem.orderId eq order.id)

// NOT EXISTS
orderItem.notExists(orderItem.orderId eq order.id)
sql
EXISTS (SELECT 1 FROM order_item WHERE order_item.order_id = order.id)
NOT EXISTS (SELECT 1 FROM order_item WHERE order_item.order_id = order.id)

Null 처리

vararg의 null predicate는 자동으로 필터링됩니다.


선택적 구현

8개의 인터페이스를 모두 사용할 필요는 없습니다. 필요한 것만 선택하세요:

옵션 1: QuerydslRepository (전체 포함)

kotlin
@Repository
class MyRepository : QuerydslRepository<MyEntity>() {
    // 8개 인터페이스 모두 사용 가능
}

옵션 2: QuerydslSupport + 선택한 인터페이스

kotlin
@Repository
class MyRepository : QuerydslSupport<MyEntity>(),
    SimpleExpressionExtensions,
    StringExpressionExtensions {

    override val domainClass = MyEntity::class.java

    // eq, ne, in, notIn, contains, startsWith 등만 사용 가능
    // number/comparable/temporal/collection 확장은 없음
}

옵션 3: 아무 클래스에서 구현

kotlin
class PredicateBuilder : BooleanExpressionExtensions, SimpleExpressionExtensions {
    // 리포지토리뿐만 아니라 어디서든 확장 함수 사용 가능
}

QuerydslRepository vs QuerydslSupport

QuerydslRepository<T>QuerydslSupport<T>
확장 인터페이스8개 전체 포함없음 (필요한 것만 추가)
domainClass제네릭을 통해 자동 추론수동 오버라이드 필요
사용 시기전체 기능이 필요할 때최소한의 API 표면만 원할 때