ORM для Kotlin: Exposed vs Ktor vs Ktorm — сравнение и примеры

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

ORM для Kotlin: сравнение и примеры

Kotlin — современный язык, который активно используется для разработки backend-приложений, Android-приложений и даже кроссплатформенных проектов. Для работы с базами данных разработчики часто выбирают ORM (Object-Relational Mapping), чтобы избежать написания сырых SQL-запросов и ускорить разработку. В этой статье мы рассмотрим три популярных ORM-инструмента для Kotlin: Exposed, Ktor (в связке с Exposed) и Ktorm. Сравним их синтаксис, производительность и удобство использования на практических примерах.

1. Exposed — официальный ORM от JetBrains

Exposed — это ORM-библиотека, разработанная компанией JetBrains специально для Kotlin. Она поддерживает два подхода: DSL (Domain Specific Language) и DAO (Data Access Objects). Exposed идеально интегрируется с фреймворком Ktor, но может использоваться и отдельно.

Ключевые особенности:

  • Лёгкий и интуитивно понятный DSL-синтаксис.
  • Поддержка транзакций, миграций и соединений.
  • Работа с PostgreSQL, MySQL, H2, SQLite и другими базами.
  • Активное сообщество и официальная документация.

Пример кода (DSL-подход):

import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction

// Определение таблицы
object Users : Table() {
    val id = integer("id").autoIncrement()
    val name = varchar("name", 50)
    val age = integer("age")

    override val primaryKey = PrimaryKey(id)
}

// Пример запроса
fun main() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")

    transaction {
        SchemaUtils.create(Users)

        // Вставка
        Users.insert {
            it[name] = "Alice"
            it[age] = 30
        }

        // Выборка
        val result = Users.selectAll().where { Users.age greater 25 }
        result.forEach {
            println("${it[Users.name]}: ${it[Users.age]}")
        }
    }
}

2. Ktor + Exposed — связка для веб-приложений

Ktor — это асинхронный веб-фреймворк для Kotlin. Хотя сам Ktor не предоставляет встроенного ORM, он отлично работает в паре с Exposed. Такая связка позволяет создавать REST API с минимальным количеством кода.

Пример интеграции Ktor с Exposed:

import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.response.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction

fun Application.module() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")

    routing {
        get("/users") {
            val users = transaction {
                Users.selectAll().map {
                    mapOf("name" to it[Users.name], "age" to it[Users.age])
                }
            }
            call.respond(users)
        }
    }
}

Этот подход подходит для микросервисов и небольших проектов, где важна простота и скорость разработки.

3. Ktorm — лёгкий и производительный ORM

Ktorm — это ещё один популярный ORM для Kotlin, который ориентирован на производительность и минимализм. Он использует функциональное программирование и поддерживает как DSL, так и SQL-подобный синтаксис.

Ключевые особенности:

  • Высокая производительность за счёт отсутствия рефлексии.
  • Поддержка расширений (например, для JSON-полей).
  • Простая конфигурация и минимальные зависимости.
  • Работа с большинством популярных СУБД.

Пример кода Ktorm:

import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.*

// Определение сущности
interface User : Entity {
    companion object : Entity.Factory()
    val id: Int
    var name: String
    var age: Int
}

object Users : Table<User>("users") {
    val id = int("id").primaryKey().bindTo { it.id }
    val name = varchar("name").bindTo { it.name }
    val age = int("age").bindTo { it.age }
}

fun main() {
    val database = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")

    database.useTransaction {
        // Вставка
        val user = User {
            name = "Bob"
            age = 25
        }
        user.flushChanges()

        // Выборка через DSL
        val result = database.from(Users).select().where { Users.age greater 20 }
        result.forEach {
            println("${it[Users.name]}: ${it[Users.age]}")
        }
    }
}

Рекомендаций