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

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

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

Начать курс

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

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

Мы рассмотрим два основных подхода: использование библиотеки Exposed для SQL и фреймворка KMongo для работы с MongoDB. Все примеры кода написаны на чистом Kotlin и готовы к запуску.

1. Работа с PostgreSQL через библиотеку Exposed

1.1. Настройка зависимостей

Для начала добавьте в файл build.gradle.kts следующие зависимости:

dependencies {
    implementation("org.jetbrains.exposed:exposed-core:0.44.0")
    implementation("org.jetbrains.exposed:exposed-dao:0.44.0")
    implementation("org.jetbrains.exposed:exposed-jdbc:0.44.0")
    implementation("org.postgresql:postgresql:42.6.0")
    implementation("com.zaxxer:HikariCP:5.0.1")
}

HikariCP — это высокопроизводительный пул соединений, который мы будем использовать для управления подключениями к БД.

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

Создадим объект для управления подключением:

import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.jetbrains.exposed.sql.Database

object DatabaseFactory {
    fun init() {
        val config = HikariConfig().apply {
            driverClassName = "org.postgresql.Driver"
            jdbcUrl = "jdbc:postgresql://localhost:5432/my_db"
            username = "user"
            password = "password"
            maximumPoolSize = 10
            isAutoCommit = false
        }

        val dataSource = HikariDataSource(config)
        Database.connect(dataSource)
    }
}

1.3. Определение таблиц и CRUD-операции

Exposed использует DSL-стиль для описания таблиц. Определим простую таблицу пользователей:

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

object Users : Table() {
    val id = integer("id").autoIncrement()
    val name = varchar("name", 100)
    val email = varchar("email", 255).uniqueIndex()

    override val primaryKey = PrimaryKey(id)
}

// Вставка данных
fun insertUser(name: String, email: String) {
    transaction {
        Users.insert {
            it[Users.name] = name
            it[Users.email] = email
        }
    }
}

// Чтение всех пользователей
fun getAllUsers(): List<ResultRow> {
    return transaction {
        Users.selectAll().toList()
    }
}

// Обновление 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 }
    }
}

1.4. Особенности Exposed

  • Транзакции: Все операции выполняются внутри блока transaction.
  • Безопасность типов: Ошибки в SQL-запросах отлавливаются на этапе компиляции.
  • Миграции: Exposed поддерживает автоматическое создание таблиц через SchemaUtils.create(Users).

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

2.1. Настройка зависимостей

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

dependencies {
    implementation("org.litote.kmongo:kmongo-coroutine:4.9.0")
    implementation("org.litote.kmongo:kmongo-id:4.9.0")
}

KMongo — это современная обертка над официальным Java-драйвером MongoDB, специально адаптированная для Kotlin.

2.2. Подключение и определение модели

KMongo поддерживает сериализацию через kotlinx.serialization:

import org.litote.kmongo.*
import kotlinx.serialization.Serializable

@Serializable
data class User(
    @BsonId val id: String = ObjectId().toHexString(),
    val name: String,
    val email: String
)

// Подключение
val client = KMongo.createClient("mongodb://localhost:27017")
val database = client.getDatabase("my_db")
val usersCollection = database.getCollection<User>()

2.3. CRUD-операции с MongoDB

KMongo предоставляет естественный DSL для запросов:

import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // Create
    usersCollection.insertOne(User(name = "Alice", email = "alice@example.com"))

    // Read (все пользователи)
    val allUsers = usersCollection.find().toList()
    println(allUsers)

    // Read (с фильтром)
    val alice = usersCollection.findOne(User::name eq "Alice")
    println(alice)

    // Update
    usersCollection.updateOne(
        User::name eq "Alice",
        set(User::email, "alice_new@example.com")
    )

    // Delete
    usersCollection.deleteOne(User::name eq "Alice")
}

2.4. Особенности KMongo

  • Корутины: Все операции асинхронны и поддерживают suspend-функции.
  • Типобезопасность: Фильтры строятся через рефлексию полей данных.
  • BsonId: Аннотация для автоматической генерации ObjectId.

3. Сравнение подходов: когда что выбирать

Критерий PostgreSQL (Exposed) MongoDB (KMongo)
Структура данных Строгая схема (таблицы)

Блоги

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