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

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

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

Начать курс

Введение: Почему Kotlin — отличный выбор для работы с базами данных

Kotlin, будучи современным и лаконичным языком программирования, предоставляет разработчикам мощные инструменты для взаимодействия с различными базами данных. Благодаря полной совместимости с Java и собственной экосистеме библиотек, Kotlin позволяет писать безопасный, выразительный и производительный код для работы как с реляционными (SQL), так и с документо-ориентированными (NoSQL) хранилищами.

В этой статье мы на практических примерах разберем подключение и выполнение CRUD-операций (Create, Read, Update, Delete) для двух популярных систем управления базами данных: PostgreSQL (реляционная) и MongoDB (документная). Вы узнаете, как настроить проект, использовать современные библиотеки (Exposed для SQL и KMongo для MongoDB) и писать чистый код с учетом особенностей Kotlin.

1. Работа с PostgreSQL в Kotlin: используем библиотеку Exposed

Для работы с реляционными базами данных в Kotlin существует несколько подходов. Мы рассмотрим библиотеку Exposed — легковесную ORM-оболочку, написанную на Kotlin, которая предоставляет типобезопасный DSL (Domain-Specific Language) для формирования SQL-запросов.

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.6.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}

1.2. Создание таблицы и подключение к БД

Определим сущность "Пользователь" и соответствующую таблицу в коде:

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

// Определяем таблицу
object Users : Table() {
    val id = integer("id").autoIncrement()
    val name = varchar("name", 255)
    val email = varchar("email", 255).uniqueIndex()

    override val primaryKey = PrimaryKey(id)
}

// Data class для пользователя
data class User(val id: Int, val name: String, val email: String)

fun main() {
    // Подключение к базе данных
    Database.connect(
        url = "jdbc:postgresql://localhost:5432/mydb",
        driver = "org.postgresql.Driver",
        user = "postgres",
        password = "password"
    )

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

1.3. CRUD-операции

Теперь реализуем базовые операции создания, чтения, обновления и удаления записей:

fun createUser(name: String, email: String): User {
    return transaction {
        val insertId = Users.insert {
            it[Users.name] = name
            it[Users.email] = email
        } get Users.id

        User(
            id = insertId,
            name = name,
            email = email
        )
    }
}

fun readUserById(id: Int): User? {
    return transaction {
        Users.selectAll().where { Users.id eq id }
            .singleOrNull()
            ?.let { row ->
                User(
                    id = row[Users.id],
                    name = row[Users.name],
                    email = row[Users.email]
                )
            }
    }
}

fun updateUserEmail(id: Int, newEmail: String) {
    transaction {
        Users.update({ Users.id eq id }) {
            it[email] = newEmail
        }
    }
}

fun deleteUser(id: Int) {
    transaction {
        Users.deleteWhere { Users.id eq id }
    }
}

Благодаря Exposed, Kotlin-код получается лаконичным и безопасным по типам. Ошибки в названиях столбцов или таблиц отлавливаются на этапе компиляции.

2. Работа с MongoDB в Kotlin: библиотека KMongo

MongoDB — это NoSQL база данных, хранящая данные в формате, близком к JSON (BSON). Для Kotlin отлично подходит библиотека KMongo, которая предоставляет корутиновую поддержку и типобезопасные билдеры для запросов.

2.1. Подключение зависимостей

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

dependencies {
    implementation("org.litote.kmongo:kmongo-coroutine-serialization:4.9.0")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}

2.2. Определение модели данных и подключение

Используем kotlinx.serialization для автоматической сериализации/десериализации:

import kotlinx.serialization.Serializable
import org.litote.kmongo.coroutine.*
import org.litote.kmongo.reactivestreams.KMongo

@Serializable
data class Product(
    val id: String = "",
    val name: String,
    val price: Double,
    val inStock: Boolean = true
)

suspend fun main() {
    // Подключение к MongoDB
    val client = KMongo.createClient("mongodb://localhost:27017")
    val database = client.getDatabase("shop")
    val productsCollection = database.getCollection<Product>("products")
}

2.3. CRUD-операции с использованием корутин

KMongo полностью поддерживает корутины Kotlin, что делает асинхронный код простым и читаемым:

suspend fun insertProduct(collection: CoroutineCollection<Product>, product: Product) {
    collection.insertOne(product)
    println("Товар '${product.name}' добавлен с ID: ${product.id}")
}

suspend fun findProductByName(collection: CoroutineCollection<Product>, name: String): Product? {
    return collection.findOne(Product::name eq name)
}

suspend fun updateProductPrice(
    collection: CoroutineCollection<Product>,
    productId: String,
    newPrice: Double
) {
    collection.updateOne(
        Product::id eq productId,
        set(Product::price setTo newPrice)
    )
}

suspend fun deleteOutOfStockProducts(collection: CoroutineCollection<Product>) {
    val result = collection.deleteMany(Product::inStock eq false)
    println("Удалено ${result.deletedCount} товаров, которых нет в наличии.")
}

Обратите внимание на использование Product::name eq — это типобезопасные фильтры KMongo, которые исключают опечатки в названиях полей.

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

Выбор между PostgreSQL и MongoDB зависит от структуры ваших данных. Вот несколько практических рекомендаций:

Блоги

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