Введение: Почему 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) |
|---|---|---|
| Структура данных | Строгая схема (таблицы) |
БлогиКлиентский IP в PHP: как не ошибиться в 2025Интеграционное тестирование C++ с Docker: от А до ЯPDO в PHP: спасает ли он от SQL-инъекций на 100%?Рекомендаций по книгамЧитайте также |