Українська
Таблиці та DSL-запити
Опис таблиці та обмежень
object Books : Table("books") є описом схеми, а не колекцією записів. Властивість varchar("title", 120) описує стовпець. nullable() дозволяє SQL NULL; default задає типове значення; uniqueIndex() забороняє дублікати на рівні бази. Перевірка форми перед вставкою корисна, але не замінює обмеження БД.
integer, long, bool, decimal, date та enumerationByName представляють різні типи. Гроші доцільно зберігати цілими копійками або точним decimal, а не double. Для дати без часу придатний java.time.LocalDate; для моменту в різних часових поясах треба явно обрати контракт зберігання.
IntIdTable надає типовий числовий ключ, який у DSL є EntityID<Int>. Його .value – звичайний Int. У власній Table первинний ключ задають через PrimaryKey. Не змішуйте зовнішній номер документа з технічним автогенерованим ключем.
Приклад 1. Підключення та схема
Кожний повний приклад лекції є окремою програмою. Для прозорого повторення цей приклад використовує свою тимчасову базу; у реальному каталозі замість неї передають сталий шлях користувача. maxAttempts = 1 робить навчальну поведінку помилки однозначною.
kotlin
import java.nio.file.Files
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.jdbc.*
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
object Shelves : Table("shelves") {
val id = integer("id").autoIncrement()
val name = varchar("name", 80).uniqueIndex()
override val primaryKey = PrimaryKey(id)
}
fun main() {
val path = Files.createTempFile("shelves-", ".db")
val db = Database.connect(
"jdbc:sqlite:$path?foreign_keys=on", "org.sqlite.JDBC"
)
transaction(db) {
maxAttempts = 1
SchemaUtils.create(Shelves)
Shelves.insert { it[name] = "Поезія" }
println("Полиць: ${Shelves.selectAll().count()}")
}
path.toFile().deleteOnExit()
}text
Полиць: 1Створення схеми в транзакції зручне для першої вправи. SchemaUtils.create не є універсальним механізмом оновлення наявної схеми. Зміна Kotlin-властивості не переносить автоматично дані зі старого стовпця й не виправляє старі значення.
Для діагностики додайте в транзакцію addLogger(StdOutSqlLogger) з імпортом org.jetbrains.exposed.v1.core.StdOutSqlLogger. Журнал допомагає побачити кількість запитів. У виробничій системі не слід безконтрольно журналювати особисті дані та параметри.

Рис. 14.4. SQL, згенерований Exposed
DSL: створення, читання, зміна й видалення
insert повертає результат вставки; з нього читають згенерований ключ. Для IntIdTable зручний insertAndGetId. batchInsert виражає вставку набору рядків, проте фактичне пакетування залежить від драйвера. Не слід припускати, що тисяча викликів insert автоматично перетвориться на один SQL-запит.
selectAll().where { ... } вибирає рядки; select(columns) обмежує стовпці. Умови eq, greater, like, and, or утворюють дерево SQL-виразу. Значення передаються параметрами, а не склеюванням рядка SQL. Якщо потрібний буквальний пошук, пам’ятайте: % і _ у LIKE є шаблонними символами.
Без orderBy порядок рядків не гарантований. Навіть якщо SQLite сьогодні повернула їх у порядку вставки, індекс або новий план запиту може змінити результат. Для пагінації задавайте стабільне сортування з унікальним додатковим ключем. limit без сортування не означає «найновіші» записи.
update(where = { ... }) та deleteWhere { ... } повертають кількість змінених рядків. Перевіряйте її для операції за id: нуль означає, що об’єкт уже відсутній. Помилка в умові може змінити багато рядків; попередній перегляд і транзакція корисні для масових навчальних операцій.
upsert поєднує вставку й оновлення при конфлікті ключа. Його поведінка залежить від діалекту та унікальних обмежень. Перш ніж застосовувати, визначте, який саме ключ позначає «той самий об’єкт» і які поля дозволено перезаписати.