Введение в работу с базами данных в Kotlin
Kotlin — современный статически типизированный язык, работающий на JVM, который стремительно набирает популярность благодаря лаконичности и безопасности. Одним из ключевых навыков для любого разработчика является умение взаимодействовать с базами данных. В этой статье мы рассмотрим, как подключаться и выполнять основные операции (CRUD) с двумя самыми популярными типами БД: реляционной PostgreSQL и документо-ориентированной MongoDB.
Мы будем использовать современные библиотеки, максимально адаптированные под идиоматический Kotlin:
- Exposed — ORM-фреймворк от JetBrains для SQL-баз данных (PostgreSQL, MySQL и др.).
- KMongo — реактивный ODM-клиент для MongoDB, построенный на корутинах.
Статья рассчитана на начинающих: мы разберём настройку проекта, подключение к БД и простые примеры вставки, чтения, обновления и удаления данных.
1. Настройка проекта и подключение зависимостей
Для начала создайте новый проект Kotlin (например, через IntelliJ IDEA с Gradle или Maven). Добавьте необходимые зависимости в файл build.gradle.kts.
Gradle зависимости для PostgreSQL (Exposed)
dependencies {
// Exposed
implementation("org.jetbrains.exposed:exposed-core:0.44.1")
implementation("org.jetbrains.exposed:exposed-dao:0.44.1")
implementation("org.jetbrains.exposed:exposed-jdbc:0.44.1")
// Драйвер PostgreSQL
implementation("org.postgresql:postgresql:42.7.1")
// Пул соединений (HikariCP)
implementation("com.zaxxer:HikariCP:5.1.0")
}
Gradle зависимости для MongoDB (KMongo)
dependencies {
// KMongo (с корутинами)
implementation("org.litote.kmongo:kmongo-coroutine:4.11.0")
// Ktor Client (если нужен реактивный драйвер)
implementation("org.litote.kmongo:kmongo-async:4.11.0")
}
После синхронизации проекта можно приступать к подключению.
2. Работа с PostgreSQL через Exposed
Exposed предоставляет два подхода: DSL (типобезопасные SQL-запросы) и DAO (объектно-реляционное отображение). Мы рассмотрим DAO — он более интуитивен для новичков.
Подключение к базе данных
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
fun connectToPostgres() {
val config = HikariConfig().apply {
jdbcUrl = "jdbc:postgresql://localhost:5432/mydb"
driverClassName = "org.postgresql.Driver"
username = "user"
password = "password"
maximumPoolSize = 10
}
val dataSource = HikariDataSource(config)
Database.connect(dataSource)
println("Подключение к PostgreSQL установлено!")
}
Определение таблицы и сущности
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
// Определяем таблицу
object UsersTable : IntIdTable("users") {
val name = varchar("name", 100)
val email = varchar("email", 255).uniqueIndex()
val age = integer("age").default(18)
}
// Определяем Entity (DAO)
class UserEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<UserEntity>(UsersTable)
var name by UsersTable.name
var email by UsersTable.email
var age by UsersTable.age
}
CRUD операции
fun performCRUD() {
transaction {
// Создание таблицы (если не существует)
SchemaUtils.create(UsersTable)
// INSERT
val newUser = UserEntity.new {
name = "Иван Петров"
email = "ivan@example.com"
age = 25
}
println("Создан пользователь с ID: ${newUser.id}")
// SELECT — все пользователи
val allUsers = UserEntity.all().toList()
allUsers.forEach { user ->
println("${user.id}: ${user.name}, ${user.email}, ${user.age}")
}
// UPDATE — изменить возраст
val userToUpdate = UserEntity.findById(1)
userToUpdate?.apply {
age = 30
flush() // сохраняем изменения
println("Возраст пользователя ${name} обновлён на $age")
}
// DELETE — удалить пользователя
val userToDelete = UserEntity.findById(2)
userToDelete?.delete()
println("Пользователь с ID 2 удалён")
}
}
Важно: Все операции выполняются внутри блока transaction, который автоматически управляет транзакциями.
3. Работа с MongoDB через KMongo
MongoDB — документная БД, где данные хранятся в формате JSON-подобных документов. KMongo предоставляет типобезопасный API для Kotlin с поддержкой корутин.
Подключение к MongoDB
import org.litote.kmongo.coroutine.coroutine
import org.litote.kmongo.reactivestreams.KMongo
import kotlinx.coroutines.runBlocking
fun connectToMongo() {
val client = KMongo.createClient("mongodb://localhost:27017")
.coroutine
val database = client.getDatabase("testdb")
println("Подключение к MongoDB установлено! База: ${database.name}")
}
Определение модели данных
import org.bson.codecs.pojo.annotations.BsonId
import org.bson.types.ObjectId
data class User(
@BsonId val id: ObjectId = ObjectId(),
val name: String,
val email: String,
val age: Int = 18
)
Аннотация @BsonId указывает, что поле id является первичным ключом.
CRUD операции с корутинами
import kotlinx.coroutines.runBlocking
import org.litote.kmongo.coroutine.*
fun performMongoCRUD() = runBlocking {
val client = KMongo.createClient().coroutine
val db = client.getDatabase("testdb")
val usersCollection = db.getCollection<User>()
// INSERT
val newUser = User(name = "Анна Смирнова", email = "anna@example.com", age = 28)
usersCollection.insertOne(newUser)
println("Документ вставлен с ID: ${newUser.id}")
// SELECT — найти всех пользователей старше 20 лет
val filter = User::age gt 20
val