Hibernate Reactive supports

Kotlin JDSL์€ Hibernate Reactive๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก hibernate-reactive-support ๋ชจ๋“ˆ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ชจ๋“ˆ์€ Hibernate Reactive์˜ Session ๋ฐ StatelessSession ๊ฐ์ฒด์— ๋Œ€ํ•œ ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜์—ฌ, Kotlin JDSL ์ฟผ๋ฆฌ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ „๋‹ฌํ•˜์—ฌ ๋ฆฌ์•กํ‹ฐ๋ธŒ ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ ์‹คํ–‰ํ•˜๊ธฐ

์ œ๊ณต๋˜๋Š” ์ฃผ์š” ํ™•์žฅ ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • createQuery(): Session๊ณผ StatelessSession ๋ชจ๋‘์—์„œ SELECT ๊ตฌ๋ฌธ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. StatelessSession์˜ ๊ฒฝ์šฐ, UPDATE์™€ DELETE ๊ตฌ๋ฌธ์—๋„ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • createMutationQuery(): ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋Š”(stateful) Session์—์„œ UPDATE์™€ DELETE ๊ตฌ๋ฌธ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋“ค์€ ๋ณดํ†ต SessionFactory๋กœ๋ถ€ํ„ฐ ์–ป๋Š” Session ๋˜๋Š” StatelessSession ์ธ์Šคํ„ด์Šค์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

Mutiny

Stateful Session (์ƒํƒœ ๊ธฐ๋ฐ˜ ์„ธ์…˜)

์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋Š” Mutiny.Session์—์„œ๋Š” SELECT ๊ตฌ๋ฌธ์— createQuery๋ฅผ, UPDATE/DELETE ๊ตฌ๋ฌธ์— createMutationQuery๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

import com.linecorp.kotlinjdsl.support.hibernate.reactive.extension.createQuery
import com.linecorp.kotlinjdsl.support.hibernate.reactive.extension.createMutationQuery
import io.smallrye.mutiny.Uni
import org.hibernate.reactive.mutiny.Mutiny

val sessionFactory: Mutiny.SessionFactory = ...
val context = JpqlRenderContext()

// ๊ฒฐ๊ณผ ๋ชฉ๋ก ์กฐํšŒ
val selectQuery = jpql {
    select(
        entity(Book::class)
    ).from(
        entity(Book::class)
    )
}
val books: Uni<List<Book>> = sessionFactory.withSession { session ->
    session.createQuery(selectQuery, context).resultList
}

// Update ๊ตฌ๋ฌธ ์‹คํ–‰
val updateQuery = jpql {
    update(
        entity(Book::class)
    ).set(
        path(Book::price), BookPrice(10)
    ).where(
        path(Book::isbn).eq(Isbn("01"))
    )
}
val updatedRows: Uni<Int> = sessionFactory.withTransaction { session, _ ->
    session.createMutationQuery(updateQuery, context).executeUpdate()
}

Stateless Session (์ƒํƒœ ๋น„์ €์žฅ ์„ธ์…˜)

Mutiny.StatelessSession์—์„œ๋Š” ๋ชจ๋“  ์ข…๋ฅ˜์˜ ๊ตฌ๋ฌธ(SELECT, UPDATE, DELETE)์— createQuery ํ™•์žฅ ํ•จ์ˆ˜๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

import com.linecorp.kotlinjdsl.support.hibernate.reactive.extension.createQuery

// Stateless ์„ธ์…˜์œผ๋กœ delete ๊ตฌ๋ฌธ ์‹คํ–‰
val deleteQuery = jpql {
    deleteFrom(
        entity(Book::class)
    ).where(
        path(Book::isbn).eq(Isbn("01"))
    )
}
val deletedRows: Uni<Int> = sessionFactory.withStatelessTransaction { session, _ ->
    session.createQuery(deleteQuery, context).executeUpdate()
}

Stage

Stage.Session๊ณผ Stage.StatelessSession์˜ ์‚ฌ์šฉ ํŒจํ„ด์€ Mutiny์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

Stateful Session (์ƒํƒœ ๊ธฐ๋ฐ˜ ์„ธ์…˜)

import com.linecorp.kotlinjdsl.support.hibernate.reactive.extension.createQuery
import com.linecorp.kotlinjdsl.support.hibernate.reactive.extension.createMutationQuery
import java.util.concurrent.CompletionStage
import org.hibernate.reactive.stage.Stage

val sessionFactory: Stage.SessionFactory = ...
val context = JpqlRenderContext()

// ๊ฒฐ๊ณผ ๋ชฉ๋ก ์กฐํšŒ
val selectQuery = jpql {
    select(
        entity(Book::class)
    ).from(
        entity(Book::class)
    )
}
val books: CompletionStage<List<Book>> = sessionFactory.withSession { session ->
    session.createQuery(selectQuery, context).resultList
}

// Delete ๊ตฌ๋ฌธ ์‹คํ–‰
val deleteQuery = jpql {
    deleteFrom(
        entity(Book::class)
    ).where(
        path(Book::isbn).eq(Isbn("01"))
    )
}
val deletedRows: CompletionStage<Int> = sessionFactory.withTransaction { session, _ ->
    session.createMutationQuery(deleteQuery, context).executeUpdate()
}

Stateless Session (์ƒํƒœ ๋น„์ €์žฅ ์„ธ์…˜)

import com.linecorp.kotlinjdsl.support.hibernate.reactive.extension.createQuery

// Stateless ์„ธ์…˜์œผ๋กœ delete ๊ตฌ๋ฌธ ์‹คํ–‰
val deleteQuery = jpql {
    deleteFrom(
        entity(Book::class)
    ).where(
        path(Book::isbn).eq(Isbn("01"))
    )
}
val deletedRows: CompletionStage<Int> = sessionFactory.withStatelessTransaction { session, _ ->
    session.createQuery(deleteQuery, context).executeUpdate()
}

Fetch ์ „๋žต๊ณผ ์„ธ์…˜ ๋ฒ”์œ„์— ๋Œ€ํ•œ ์ฐธ๊ณ ์‚ฌํ•ญ

๋ฆฌ์•กํ‹ฐ๋ธŒ ์„ธ์…˜์˜ ๋ฒ”์œ„๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ withSession์ด๋‚˜ withTransaction๊ณผ ๊ฐ™์€ ๋ฉ”์„œ๋“œ์˜ ๋žŒ๋‹ค ๋ธ”๋ก์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. ๋ฆฌ์•กํ‹ฐ๋ธŒ ์ŠคํŠธ๋ฆผ์ด ์™„๋ฃŒ๋˜๊ณ  ๋ธ”๋ก ์‹คํ–‰์ด ๋๋‚˜๋ฉด ์„ธ์…˜์€ ๋‹ซํž™๋‹ˆ๋‹ค.

์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•: ์„ธ์…˜ ๋ฒ”์œ„ ๋‚ด์—์„œ ์ง€์—ฐ ๋กœ๋”ฉ๋œ ์—ฐ๊ด€ ๊ด€๊ณ„ ์ ‘๊ทผํ•˜๊ธฐ

์„ธ์…˜ ๋ฒ”์œ„ ๋‚ด์—์„œ ์ ‘๊ทผํ•˜๋Š” ํ•œ, ์ง€์—ฐ ๋กœ๋”ฉ๋œ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

val query = jpql {
    select(
        entity(Book::class)
    ).from(
        entity(Book::class)
    )
}

val bookAuthorSizes: Uni<List<Int>> = sessionFactory.withSession { session ->
    session.createQuery(query, context).resultList.onItem().transform { bookList ->
        // ์„ธ์…˜์ด ์•„์ง ํ™œ์„ฑ ์ƒํƒœ์ด๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ book.authors์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์€ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.
        bookList.map { it.authors.size }
    }
}

ํ•„์ˆ˜์ ์ธ ๋ฐฉ๋ฒ•: ์„ธ์…˜ ๋ฒ”์œ„ ๋ฐ–์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด fetch join ์‚ฌ์šฉํ•˜๊ธฐ

์„ธ์…˜์ด ๋‹ซํžŒ ํ›„์— (์˜ˆ: ๋ฆฌ์•กํ‹ฐ๋ธŒ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ) ์—ฐ๊ด€ ๊ด€๊ณ„์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, fetch join์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ˜๋“œ์‹œ ์ฆ‰์‹œ ๋กœ๋”ฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// fetch join์„ ์‚ฌ์šฉํ•˜์—ฌ authors๋ฅผ ์ฆ‰์‹œ ๋กœ๋”ฉํ•ฉ๋‹ˆ๋‹ค.
val query = jpql {
    select(
        distinct(entity(Book::class))
    ).from(
        entity(Book::class),
        fetchJoin(Book::authors) // authors ์ปฌ๋ ‰์…˜์„ ์ฆ‰์‹œ fetchํ•ฉ๋‹ˆ๋‹ค.
    )
}

val books: Uni<List<Book>> = sessionFactory.withSession { session ->
    session.createQuery(query, context).resultList
}

// authors ์ปฌ๋ ‰์…˜์ด ์ฆ‰์‹œ ๋กœ๋”ฉ๋˜์—ˆ์œผ๋ฏ€๋กœ ์ด์ œ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.
books.onItem().invoke { bookList ->
    bookList.forEach { book ->
        println(book.authors.size)
    }
}

Last updated