Expressions

Kotlin JDSL์€ JPQL์˜ expression๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด Expression ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Alias

Expression์˜ as()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์œผ๋กœ Expression์— alias๋ฅผ ๊ฑธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. expression()์„ ์ด์šฉํ•˜๋ฉด Expression์˜ ์ฐธ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐธ์กฐ๋Š” alias๋ฅผ ํ†ตํ•ด์„œ ๊ตฌ๋ถ„๋˜๋ฉฐ ๋™์ผํ•œ alias๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Expression์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด Expression์— alias๋ฅผ ๊ฑธ๊ณ  alias๊ฐ€ ๊ฑธ๋ฆฐ Expression์„ ๋‹ค๋ฅธ clause์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

val bookPrice = expression(BigDecimal::class, "price")

select(
    path(Book::price)(BookPrice::value).`as`(bookPrice)
).from(
    entity(Book::class)
).where(
    bookPrice.eq(BigDecimal.valueOf(100))
)

// OR

select(
    path(Book::price)(BookPrice::value).`as`(expression("price"))
).from(
    entity(Book::class)
).where(
    expression(BigDecimal::class, "price").eq(BigDecimal.valueOf(100))
)

Type Cast

์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” Expression์˜ ํƒ€์ž…์„ ์›ํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Kotlin JDSL์€ as()๋ฅผ ํ†ตํ•ด์„œ unsafe type casting์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

This is a shortened form of as Expression<T>, so it may not work as expected.

avg(path(FullTimeEmployee::annualSalary)(EmployeeSalary::value)).`as`(BigDecimal::class)

Arithmetic operations

์‚ฐ์ˆ  ์—ฐ์‚ฐ์ž๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • + (plus)

  • - (minus)

  • * (times)

  • / (div)

path(Book::price).plus(path(Book::salePrice))
plus(path(Book::price), path(Book::salePrice))

path(Book::price).minus(path(Book::salePrice))
minus(path(Book::price), path(Book::salePrice))

path(Book::price).times(path(Book::salePrice))
times(path(Book::price), path(Book::salePrice))

path(Book::price).div(path(Book::salePrice))
div(path(Book::price), path(Book::salePrice))

Parentheses

ํ™•์žฅ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ์ผ๋ฐ˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์‚ฐ์ˆ  ์—ฐ์‚ฐ์ž์— ์—ฐ์‚ฐ ์ˆœ์„œ๋ฅผ ์œ„ํ•œ ์†Œ๊ด„ํ˜ธ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ™•์žฅ ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ ์—ฐ์‚ฐ ์ˆœ์„œ๊ฐ€ ๋ชจํ˜ธํ•ด์„œ ์†Œ๊ด„ํ˜ธ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

// ์ผ๋ฐ˜ ํ•จ์ˆ˜: (Book.price - Book.salePrice) * (100)
times(
    path(Book::price).minus(path(Book::salePrice)),
    BigDecimal.valueOf(100),
)

// ํ™•์žฅ ํ•จ์ˆ˜: Book.price - Book.salePrice * 100
path(Book::price).minus(path(Book::salePrice)).times(BigDecimal.valueOf(100))

Values

๊ฐ’์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, value()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฐ’์€ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์น˜ํ™˜๋˜๋ฉฐ, ์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ KClass๊ฐ€ value()์— ์ „๋‹ฌ๋˜๋ฉด ์ด๋Š” Entity๋กœ ์ธ์‹๋ฉ๋‹ˆ๋‹ค.

// SELECT Book.isbn FROM Book as Book WHERE Book.price = ?
select(
    path(Book::isbn)
).from(
    entity(Book::class)
).where(
    path(Book::price).eq(value(BigDecimal.valueOf(100)))
)

Params

์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, value() ๋Œ€์‹  param()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. param()์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

val context = JpqlRenderContext()

val query = jpql {
    select(
        path(Book::isbn)
    ).from(
        entity(Book::class)
    ).where(
        path(Book::price).eq(param("price"))
    )
}

val queryParams = mapOf(
    "price" to BigDecimal.valueOf(100)
)

entityManager.createQuery(query, queryParams, context)

Literals

literal์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, value() ๋Œ€์‹  xxxLiteral()์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

string literal์„ ์ถœ๋ ฅํ•  ๋•Œ ๋งŒ์•ฝ '(์ž‘์€ ๋”ฐ์˜ดํ‘œ)๊ฐ€ ์žˆ์œผ๋ฉด '(์ž‘์€ ๋”ฐ์˜ดํ‘œ)๋Š” ''(์ž‘์€ ๋”ฐ์˜ดํ‘œ 2๊ฐœ)๋กœ ๋ณ€๊ฒฝ๋˜์–ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋กœ 'literal''s' ์ฒ˜๋Ÿผ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

Aggregation functions

์ง‘ํ•ฉ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, ๋‹ค์Œ ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • COUNT (count)

  • MIN (min)

  • MAX (max)

  • AVG (avg)

  • SUM (sum)

count(path(Book::price))
countDistinct(path(Book::price))

max(path(Book::price))
maxDistinct(path(Book::price))

min(path(Book::price))
minDistinct(path(Book::price))

sum(path(Book::price))
sumDistinct(path(Book::price))

Sum

sum()์€ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Functions

Kotlin JDSL์€ JPA์—์„œ ์ œ๊ณตํ•˜๋Š” ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋“ค์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

String functions

  • CONCAT (concat)

  • SUBSTRING (substring)

  • TRIM (trim)

  • LOWER (lower)

  • UPPER (upper)

  • LENGTH (length)

  • LOCATE (locate)

concat(path(Book::title), literal(":"), path(Book::imageUrl))

substring(path(Book::title), 4)

trim(path(Book::title))
trim('B').from(path(Book::title))

lower(path(Book::title))

upper(path(Book::title))

length(path(Book::title))

locate("Book", path(Book::title))

Arithmetic functions

  • ABS (abs)

  • CEILING (ceiling)

  • EXP (exp)

  • FLOOR (floor)

  • INDEX (index)

  • LN (ln)

  • MOD (mod)

  • POWER (power)

  • SIGN (sign)

  • SQRT (sqrt)

  • ROUND (round)

  • SIZE (size)

abs(path(Book::price))

ceiling(path(Book::price))

exp(path(Book::price))

floor(path(Book::price))

index(BookAuthor::class)

ln(path(Book::price))

mod(path(Employee::age), 3)

power(path(Employee::age), 2)

sign(path(Book::price))

sqrt(path(Book::price))

round(path(Book::price), 2)

size(path(Book::authors))

Datetime functions

  • CURRENT_DATE (currentDate)

  • CURRENT_TIME (currentTime)

  • CURRENT_TIMESTAMP (currentTimestamp)

  • LOCAL DATE (localDate)

  • LOCAL TIME (localTime)

  • LOCAL DATETIME (localDateTime)

currentDate()

currentTime()

currentTimestamp()

localDate()

localTime()

localDateTime()

Database function

DB ํ•จ์ˆ˜๋‚˜ ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, function()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function(String::class, "myFunction", path(Book::isbn))

์‚ฌ์šฉํ•  ํ•จ์ˆ˜์˜ ์ •๋ณด๋ฅผ JPA ์ œ๊ณต์ž์— ๋“ฑ๋กํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Hibernate๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด FunctionContributor๋ฅผ ๋ฐ˜๋“œ์‹œ ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Cases

case๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, caseWhen()๊ณผ caseValue()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

caseWhen(path(Book::price).lt(BigDecimal.valueOf(100))).then("0")
    .`when`(path(Book::price).lt(BigDecimal.valueOf(200))).then("100")
    .`when`(path(Book::price).lt(BigDecimal.valueOf(300))).then("200")
    .`else`("300")

caseValue(path(Book::price))
    .`when`(BigDecimal.valueOf("100")).then("10")
    .`when`(BigDecimal.valueOf("200")).then("20")
    .`when`(BigDecimal.valueOf("300")).then("30")
    .`else`(path(Book::price))

Coalesce

coalesce๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, coalesce()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

coalesce(path(Employee::nickname), path(Employee::name))

NullIf

nullIf๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, nullIf()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

nullIf(path(Book::price), BigDecimal.ZERO)

New

DTO ํ”„๋กœ์ ์…˜์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, new()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

data class Row(
    val departmentId: Long,
    val count: Long,
)

new(
    Row::class
    path(EmployeeDepartment::departmentId),
    count(Employee::employeeId),
)

Type

type ์—ฐ์‚ฐ์ž๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, type()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

select(
    path(Employee::id)
).from(
    entity(Employee::class)
).where(
    type(entity(Employee::class)).eq(FullTimeEmployee::class)
)

Custom expression

์ปค์Šคํ…€ expression์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด, customExpression()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

customExpression(String::class, "CAST({0} AS VARCHAR)", path(Book::price))

๋งŒ์•ฝ customExpression()์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋‚˜๋งŒ์˜ DSL์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด๋ณด์„ธ์š”.

Last updated