Работа с базами данных в Kotlin: PostgreSQL и MongoDB

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

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

Начать курс

Введение: Зачем Kotlin для работы с базами данных?

Kotlin — современный статически типизированный язык, который полностью совместим с Java и активно используется для создания бэкенд-приложений. Благодаря лаконичному синтаксису, null-безопасности и мощным инструментам, Kotlin идеально подходит для работы с реляционными (PostgreSQL) и документо-ориентированными (MongoDB) базами данных. В этой статье мы разберём практические примеры подключения, выполнения CRUD-операций и оптимизации запросов.

Мы рассмотрим две популярные библиотеки: Exposed (для PostgreSQL) и KMongo (для MongoDB). Вы научитесь настраивать окружение, писать эффективные запросы и избегать типичных ошибок.

1. Работа с PostgreSQL через Exposed

Exposed — это ORM-библиотека для Kotlin, которая предоставляет два API: DSL (Domain-Specific Language) для написания запросов в стиле SQL и DAO (Data Access Object) для объектно-ориентированного подхода. Рассмотрим оба варианта.

1.1. Подключение и настройка

Добавьте зависимости в build.gradle.kts:

dependencies {
    implementation("org.jetbrains.exposed:exposed-core:0.41.1")
    implementation("org.jetbrains.exposed:exposed-dao:0.41.1")
    implementation("org.jetbrains.exposed:exposed-jdbc:0.41.1")
    implementation("org.postgresql:postgresql:42.5.0")
}

Подключение к базе данных:

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

fun main() {
    Database.connect(
        url = "jdbc:postgresql://localhost:5432/mydb",
        driver = "org.postgresql.Driver",
        user = "user",
        password = "password"
    )
    println("Подключение к PostgreSQL установлено")
}

1.2. Создание таблицы и CRUD-операции (DSL)

Определим таблицу Users:

import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.update

object Users : Table() {
    val id = integer("id").autoIncrement()
    val name = varchar("name", 50)
    val email = varchar("email", 100).uniqueIndex()
    override val primaryKey = PrimaryKey(id)
}

Выполним CRUD-операции:

transaction {
    // Создание таблицы
    SchemaUtils.create(Users)

    // INSERT
    Users.insert {
        it[name] = "Алексей"
        it[email] = "alex@example.com"
    }

    // SELECT
    val users = Users.selectAll().map {
        "${it[Users.id]}: ${it[Users.name]} (${it[Users.email]})"
    }
    println(users)

    // UPDATE
    Users.update({ Users.id eq 1 }) {
        it[name] = "Алексей Иванов"
    }

    // DELETE (используем deleteWhere)
    Users.deleteWhere { Users.id eq 2 }
}

1.3. DAO-подход

Создадим сущность и DAO-класс:

import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable

object UsersDAO : IntIdTable() {
    val name = varchar("name", 50)
    val email = varchar("email", 100)
}

class User(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<User>(UsersDAO)
    var name by UsersDAO.name
    var email by UsersDAO.email
}

// Использование
transaction {
    val newUser = User.new {
        name = "Мария"
        email = "maria@example.com"
    }
    println("Создан пользователь с ID: ${newUser.id.value}")
}

2. Работа с MongoDB через KMongo

KMongo — это корутин-ориентированный драйвер для MongoDB, построенный на основе официального Java-драйвера. Он поддерживает сериализацию через kotlinx.serialization и Jackson.

2.1. Подключение KMongo

Добавьте в build.gradle.kts:

dependencies {
    implementation("org.litote.kmongo:kmongo-coroutine:4.7.0")
    implementation("org.litote.kmongo:kmongo-serialization:4.7.0")
}

Подключение к MongoDB:

import org.litote.kmongo.coroutine.coroutine
import org.litote.kmongo.reactivestreams.KMongo

suspend fun main() {
    val client = KMongo.createClient("mongodb://localhost:27017")
    val database = client.getDatabase("mydb").coroutine
    val collection = database.getCollection<User>("users")
    println("Подключение к MongoDB установлено")
}

2.2. Определение модели и CRUD

Модель данных:

import org.bson.types.ObjectId
import kotlinx.serialization.Serializable

@Serializable
data class User(
    @BsonId val id: ObjectId = ObjectId(),
    val name: String,
    val email: String,
    val age: Int? = null
)

CRUD-операции с корутинами:

suspend fun main() {
    val client = KMongo.createClient().coroutine
    val db = client.getDatabase("testdb")
    val users = db.getCollection<User>()

    // CREATE
    users.insertOne(User(name = "Иван", email = "ivan@test.com", age = 30))

    // READ (все записи)
    val allUsers: List<User> = users.find().toList()
    println(allUsers)

    // READ (с фильтром)
    val filtered = users.find(User::name eq "Иван").toList()

    // UPDATE
    users.updateOne(
        User::name eq "Иван",
        set(User::age, 31)
    )

    // DELETE
    users.deleteOne(User::email eq "ivan@test.com")
}

2.3. Агрегации и индексы

Создание индекса:

users.createIndex(User::email, unique = true)

Пример агрегации (группировка по возрасту):

val pipeline = listOf(
    group(User::age, sumCount())
)
val result = users.aggregate<AggregationResult>(pipeline).toList()

3. Сравнение подходов и лучшие практики

Выбор между PostgreSQL и MongoDB зависит от структуры данных. Если вам нужны строгие схемы, связи и транзакции — выбирайте PostgreSQL с Exposed. Если данные слабо структурированы или часто

Блоги

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